recipients) {
+ Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
+
+ for (String recipient : recipients) {
+ if (Mms.isEmailAddress(recipient)) {
+ recipient = Mms.extractAddrSpec(recipient);
+ }
+
+ uriBuilder.appendQueryParameter("recipient", recipient);
+ }
+
+ Uri uri = uriBuilder.build();
+ //if (DEBUG) Log.v(TAG, "getOrCreateThreadId uri: " + uri);
+
+ Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
+ uri, ID_PROJECTION, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ } else {
+ Log.e(TAG, "getOrCreateThreadId returned no rows!");
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString());
+ throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
+ }
+ }
+
+ /**
+ * Contains all MMS messages.
+ */
+ public static final class Mms implements BaseMmsColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://mms");
+
+ public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath(
+ CONTENT_URI, "report-request");
+
+ public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath(
+ CONTENT_URI, "report-status");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+ /**
+ * mailbox = name-addr
+ * name-addr = [display-name] angle-addr
+ * angle-addr = [CFWS] "<" addr-spec ">" [CFWS]
+ */
+ public static final Pattern NAME_ADDR_EMAIL_PATTERN =
+ Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
+
+ /**
+ * quoted-string = [CFWS]
+ * DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+ * [CFWS]
+ */
+ public static final Pattern QUOTED_STRING_PATTERN =
+ Pattern.compile("\\s*\"([^\"]*)\"\\s*");
+
+ public static final Cursor query(
+ ContentResolver cr, String[] projection) {
+ return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
+ }
+
+ public static final Cursor query(
+ ContentResolver cr, String[] projection,
+ String where, String orderBy) {
+ return cr.query(CONTENT_URI, projection,
+ where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+ }
+
+ public static final String getMessageBoxName(int msgBox) {
+ switch (msgBox) {
+ case MESSAGE_BOX_ALL:
+ return "all";
+ case MESSAGE_BOX_INBOX:
+ return "inbox";
+ case MESSAGE_BOX_SENT:
+ return "sent";
+ case MESSAGE_BOX_DRAFTS:
+ return "drafts";
+ case MESSAGE_BOX_OUTBOX:
+ return "outbox";
+ default:
+ throw new IllegalArgumentException("Invalid message box: " + msgBox);
+ }
+ }
+
+ public static String extractAddrSpec(String address) {
+ Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
+
+ if (match.matches()) {
+ return match.group(2);
+ }
+ return address;
+ }
+
+ /**
+ * Returns true if the address is an email address
+ *
+ * @param address the input address to be tested
+ * @return true if address is an email address
+ */
+ public static boolean isEmailAddress(String address) {
+ if (TextUtils.isEmpty(address)) {
+ return false;
+ }
+
+ String s = extractAddrSpec(address);
+ Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
+ return match.matches();
+ }
+
+ /**
+ * Returns true if the number is a Phone number
+ *
+ * @param number the input number to be tested
+ * @return true if number is a Phone number
+ */
+ public static boolean isPhoneNumber(String number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ Matcher match = Patterns.PHONE.matcher(number);
+ return match.matches();
+ }
+
+ /**
+ * Contains all MMS messages in the MMS app's inbox.
+ */
+ public static final class Inbox implements BaseMmsColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri
+ CONTENT_URI = Uri.parse("content://mms/inbox");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "date DESC";
+ }
+
+ /**
+ * Contains all MMS messages in the MMS app's sent box.
+ */
+ public static final class Sent implements BaseMmsColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri
+ CONTENT_URI = Uri.parse("content://mms/sent");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "date DESC";
+ }
+
+ /**
+ * Contains all MMS messages in the MMS app's drafts box.
+ */
+ public static final class Draft implements BaseMmsColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri
+ CONTENT_URI = Uri.parse("content://mms/drafts");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "date DESC";
+ }
+
+ /**
+ * Contains all MMS messages in the MMS app's outbox.
+ */
+ public static final class Outbox implements BaseMmsColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri
+ CONTENT_URI = Uri.parse("content://mms/outbox");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "date DESC";
+ }
+
+ public static final class Addr implements BaseColumns {
+ /**
+ * The ID of MM which this address entry belongs to.
+ */
+ public static final String MSG_ID = "msg_id";
+
+ /**
+ * The ID of contact entry in Phone Book.
+ */
+ public static final String CONTACT_ID = "contact_id";
+
+ /**
+ * The address text.
+ */
+ public static final String ADDRESS = "address";
+
+ /**
+ * Type of address, must be one of PduHeaders.BCC,
+ * PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO.
+ */
+ public static final String TYPE = "type";
+
+ /**
+ * Character set of this entry.
+ */
+ public static final String CHARSET = "charset";
+ }
+
+ public static final class Part implements BaseColumns {
+ /**
+ * The identifier of the message which this part belongs to.
+ * Type: INTEGER
+ */
+ public static final String MSG_ID = "mid";
+
+ /**
+ * The order of the part.
+ * Type: INTEGER
+ */
+ public static final String SEQ = "seq";
+
+ /**
+ * The content type of the part.
+ * Type: TEXT
+ */
+ public static final String CONTENT_TYPE = "ct";
+
+ /**
+ * The name of the part.
+ * Type: TEXT
+ */
+ public static final String NAME = "name";
+
+ /**
+ * The charset of the part.
+ * Type: TEXT
+ */
+ public static final String CHARSET = "chset";
+
+ /**
+ * The file name of the part.
+ * Type: TEXT
+ */
+ public static final String FILENAME = "fn";
+
+ /**
+ * The content disposition of the part.
+ * Type: TEXT
+ */
+ public static final String CONTENT_DISPOSITION = "cd";
+
+ /**
+ * The content ID of the part.
+ * Type: INTEGER
+ */
+ public static final String CONTENT_ID = "cid";
+
+ /**
+ * The content location of the part.
+ * Type: INTEGER
+ */
+ public static final String CONTENT_LOCATION = "cl";
+
+ /**
+ * The start of content-type of the message.
+ * Type: INTEGER
+ */
+ public static final String CT_START = "ctt_s";
+
+ /**
+ * The type of content-type of the message.
+ * Type: TEXT
+ */
+ public static final String CT_TYPE = "ctt_t";
+
+ /**
+ * The location(on filesystem) of the binary data of the part.
+ * Type: INTEGER
+ */
+ public static final String _DATA = "_data";
+
+ public static final String TEXT = "text";
+
+ }
+
+ public static final class Rate {
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(
+ Mms.CONTENT_URI, "rate");
+ /**
+ * When a message was successfully sent.
+ * Type: INTEGER
+ */
+ public static final String SENT_TIME = "sent_time";
+ }
+
+ public static final class Intents {
+ private Intents() {
+ // Non-instantiatable.
+ }
+
+ /**
+ * The extra field to store the contents of the Intent,
+ * which should be an array of Uri.
+ */
+ public static final String EXTRA_CONTENTS = "contents";
+ /**
+ * The extra field to store the type of the contents,
+ * which should be an array of String.
+ */
+ public static final String EXTRA_TYPES = "types";
+ /**
+ * The extra field to store the 'Cc' addresses.
+ */
+ public static final String EXTRA_CC = "cc";
+ /**
+ * The extra field to store the 'Bcc' addresses;
+ */
+ public static final String EXTRA_BCC = "bcc";
+ /**
+ * The extra field to store the 'Subject'.
+ */
+ public static final String EXTRA_SUBJECT = "subject";
+ /**
+ * Indicates that the contents of specified URIs were changed.
+ * The application which is showing or caching these contents
+ * should be updated.
+ */
+ public static final String
+ CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
+ /**
+ * An extra field which stores the URI of deleted contents.
+ */
+ public static final String DELETED_CONTENTS = "deleted_contents";
+ }
+ }
+
+ /**
+ * Contains all MMS and SMS messages.
+ */
+ public static final class MmsSms implements BaseColumns {
+ /**
+ * The column to distinguish SMS & MMS messages in query results.
+ */
+ public static final String TYPE_DISCRIMINATOR_COLUMN =
+ "transport_type";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/");
+
+ public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse(
+ "content://mms-sms/conversations");
+
+ public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse(
+ "content://mms-sms/messages/byphone");
+
+ public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse(
+ "content://mms-sms/undelivered");
+
+ public static final Uri CONTENT_DRAFT_URI = Uri.parse(
+ "content://mms-sms/draft");
+
+ public static final Uri CONTENT_LOCKED_URI = Uri.parse(
+ "content://mms-sms/locked");
+
+ /***
+ * Pass in a query parameter called "pattern" which is the text
+ * to search for.
+ * The sort order is fixed to be thread_id ASC,date DESC.
+ */
+ public static final Uri SEARCH_URI = Uri.parse(
+ "content://mms-sms/search");
+
+ // Constants for message protocol types.
+ public static final int SMS_PROTO = 0;
+ public static final int MMS_PROTO = 1;
+
+ // Constants for error types of pending messages.
+ public static final int NO_ERROR = 0;
+ public static final int ERR_TYPE_GENERIC = 1;
+ public static final int ERR_TYPE_SMS_PROTO_TRANSIENT = 2;
+ public static final int ERR_TYPE_MMS_PROTO_TRANSIENT = 3;
+ public static final int ERR_TYPE_TRANSPORT_FAILURE = 4;
+ public static final int ERR_TYPE_GENERIC_PERMANENT = 10;
+ public static final int ERR_TYPE_SMS_PROTO_PERMANENT = 11;
+ public static final int ERR_TYPE_MMS_PROTO_PERMANENT = 12;
+
+ public static final class PendingMessages implements BaseColumns {
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(
+ MmsSms.CONTENT_URI, "pending");
+ /**
+ * The type of transport protocol(MMS or SMS).
+ * Type: INTEGER
+ */
+ public static final String PROTO_TYPE = "proto_type";
+ /**
+ * The ID of the message to be sent or downloaded.
+ * Type: INTEGER
+ */
+ public static final String MSG_ID = "msg_id";
+ /**
+ * The type of the message to be sent or downloaded.
+ * This field is only valid for MM. For SM, its value is always
+ * set to 0.
+ */
+ public static final String MSG_TYPE = "msg_type";
+ /**
+ * The type of the error code.
+ * Type: INTEGER
+ */
+ public static final String ERROR_TYPE = "err_type";
+ /**
+ * The error code of sending/retrieving process.
+ * Type: INTEGER
+ */
+ public static final String ERROR_CODE = "err_code";
+ /**
+ * How many times we tried to send or download the message.
+ * Type: INTEGER
+ */
+ public static final String RETRY_INDEX = "retry_index";
+ /**
+ * The time to do next retry.
+ */
+ public static final String DUE_TIME = "due_time";
+ /**
+ * The time we last tried to send or download the message.
+ */
+ public static final String LAST_TRY = "last_try";
+ }
+
+ public static final class WordsTable {
+ public static final String ID = "_id";
+ public static final String SOURCE_ROW_ID = "source_id";
+ public static final String TABLE_ID = "table_to_use";
+ public static final String INDEXED_TEXT = "index_text";
+ }
+ }
+
+ public static final class Carriers implements BaseColumns {
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://telephony/carriers");
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "name ASC";
+
+ public static final String NAME = "name";
+
+ public static final String APN = "apn";
+
+ public static final String PROXY = "proxy";
+
+ public static final String PORT = "port";
+
+ public static final String MMSPROXY = "mmsproxy";
+
+ public static final String MMSPORT = "mmsport";
+
+ public static final String SERVER = "server";
+
+ public static final String USER = "user";
+
+ public static final String PASSWORD = "password";
+
+ public static final String MMSC = "mmsc";
+
+ public static final String MCC = "mcc";
+
+ public static final String MNC = "mnc";
+
+ public static final String NUMERIC = "numeric";
+
+ public static final String AUTH_TYPE = "authtype";
+
+ public static final String TYPE = "type";
+
+ public static final String INACTIVE_TIMER = "inactivetimer";
+
+ // Only if enabled try Data Connection.
+ public static final String ENABLED = "enabled";
+
+ // Rules apply based on class.
+ public static final String CLASS = "class";
+
+ /**
+ * The protocol to be used to connect to this APN.
+ *
+ * One of the PDP_type values in TS 27.007 section 10.1.1.
+ * For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ */
+ public static final String PROTOCOL = "protocol";
+
+ /**
+ * The protocol to be used to connect to this APN when roaming.
+ *
+ * The syntax is the same as protocol.
+ */
+ public static final String ROAMING_PROTOCOL = "roaming_protocol";
+
+ public static final String CURRENT = "current";
+
+ /**
+ * Current status of APN
+ * true : enabled APN, false : disabled APN.
+ */
+ public static final String CARRIER_ENABLED = "carrier_enabled";
+
+ /**
+ * 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).
+ */
+ public static final String BEARER = "bearer";
+ }
+
+ /**
+ * Contains received SMS cell broadcast messages.
+ */
+ public static final class CellBroadcasts implements BaseColumns {
+
+ /** Not instantiable. */
+ private CellBroadcasts() {}
+
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://cellbroadcasts");
+
+ /**
+ * Message geographical scope.
+ * Type: INTEGER
+ */
+ public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+
+ /**
+ * Message serial number.
+ * Type: INTEGER
+ */
+ public static final String SERIAL_NUMBER = "serial_number";
+
+ /**
+ * PLMN of broadcast sender. (SERIAL_NUMBER + PLMN + LAC + CID) uniquely identifies a
+ * broadcast for duplicate detection purposes.
+ * Type: TEXT
+ */
+ public static final String PLMN = "plmn";
+
+ /**
+ * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA.
+ * Only included if Geographical Scope of message is not PLMN wide (01).
+ * Type: INTEGER
+ */
+ public static final String LAC = "lac";
+
+ /**
+ * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the
+ * Geographical Scope of message is cell wide (00 or 11).
+ * Type: INTEGER
+ */
+ public static final String CID = "cid";
+
+ /**
+ * Message code (OBSOLETE: merged into SERIAL_NUMBER).
+ * Type: INTEGER
+ */
+ public static final String V1_MESSAGE_CODE = "message_code";
+
+ /**
+ * Message identifier (OBSOLETE: renamed to SERVICE_CATEGORY).
+ * Type: INTEGER
+ */
+ public static final String V1_MESSAGE_IDENTIFIER = "message_id";
+
+ /**
+ * Service category (GSM/UMTS message identifier, CDMA service category).
+ * Type: INTEGER
+ */
+ public static final String SERVICE_CATEGORY = "service_category";
+
+ /**
+ * Message language code.
+ * Type: TEXT
+ */
+ public static final String LANGUAGE_CODE = "language";
+
+ /**
+ * Message body.
+ * Type: TEXT
+ */
+ public static final String MESSAGE_BODY = "body";
+
+ /**
+ * Message delivery time.
+ * Type: INTEGER (long)
+ */
+ public static final String DELIVERY_TIME = "date";
+
+ /**
+ * Has the message been viewed?
+ * Type: INTEGER (boolean)
+ */
+ public static final String MESSAGE_READ = "read";
+
+ /**
+ * Message format (3GPP or 3GPP2).
+ * Type: INTEGER
+ */
+ public static final String MESSAGE_FORMAT = "format";
+
+ /**
+ * Message priority (including emergency).
+ * Type: INTEGER
+ */
+ public static final String MESSAGE_PRIORITY = "priority";
+
+ /**
+ * ETWS warning type (ETWS alerts only).
+ * Type: INTEGER
+ */
+ public static final String ETWS_WARNING_TYPE = "etws_warning_type";
+
+ /**
+ * CMAS message class (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
+
+ /**
+ * CMAS category (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_CATEGORY = "cmas_category";
+
+ /**
+ * CMAS response type (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
+
+ /**
+ * CMAS severity (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_SEVERITY = "cmas_severity";
+
+ /**
+ * CMAS urgency (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_URGENCY = "cmas_urgency";
+
+ /**
+ * CMAS certainty (CMAS alerts only).
+ * Type: INTEGER
+ */
+ public static final String CMAS_CERTAINTY = "cmas_certainty";
+
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
+
+ /**
+ * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
+ */
+ public static final String[] QUERY_COLUMNS = {
+ _ID,
+ GEOGRAPHICAL_SCOPE,
+ PLMN,
+ LAC,
+ CID,
+ SERIAL_NUMBER,
+ SERVICE_CATEGORY,
+ LANGUAGE_CODE,
+ MESSAGE_BODY,
+ DELIVERY_TIME,
+ MESSAGE_READ,
+ MESSAGE_FORMAT,
+ MESSAGE_PRIORITY,
+ ETWS_WARNING_TYPE,
+ CMAS_MESSAGE_CLASS,
+ CMAS_CATEGORY,
+ CMAS_RESPONSE_TYPE,
+ CMAS_SEVERITY,
+ CMAS_URGENCY,
+ CMAS_CERTAINTY
+ };
+ }
+}
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..36c238d8acd25eac3e5775aa878af864a7eb63c9
--- /dev/null
+++ b/src/java/android/telephony/CellBroadcastMessage.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.telephony;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.telephony.SmsCbCmasInfo;
+import android.telephony.SmsCbEtwsInfo;
+import android.telephony.SmsCbLocation;
+import android.telephony.SmsCbMessage;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.StyleSpan;
+
+/**
+ * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that
+ * decoded broadcast message objects can be passed between running Services.
+ * New broadcasts are received by the CellBroadcastReceiver app, which exports
+ * the database of previously received broadcasts at "content://cellbroadcasts/".
+ * The "android.permission.READ_CELL_BROADCASTS" permission is required to read
+ * from the ContentProvider, and writes to the database are not allowed.
+ *
+ * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows
+ * in the database cursor returned by the ContentProvider.
+ *
+ * {@hide}
+ */
+public class CellBroadcastMessage implements Parcelable {
+
+ /** Identifier for getExtra() when adding this object to an Intent. */
+ public static final String SMS_CB_MESSAGE_EXTRA =
+ "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE";
+
+ /** SmsCbMessage. */
+ private final SmsCbMessage mSmsCbMessage;
+
+ private final long mDeliveryTime;
+ private boolean mIsRead;
+
+ public CellBroadcastMessage(SmsCbMessage message) {
+ mSmsCbMessage = message;
+ mDeliveryTime = System.currentTimeMillis();
+ mIsRead = false;
+ }
+
+ private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) {
+ mSmsCbMessage = message;
+ mDeliveryTime = deliveryTime;
+ mIsRead = isRead;
+ }
+
+ private CellBroadcastMessage(Parcel in) {
+ mSmsCbMessage = new SmsCbMessage(in);
+ mDeliveryTime = in.readLong();
+ mIsRead = (in.readInt() != 0);
+ }
+
+ /** Parcelable: no special flags. */
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ mSmsCbMessage.writeToParcel(out, flags);
+ out.writeLong(mDeliveryTime);
+ out.writeInt(mIsRead ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public CellBroadcastMessage createFromParcel(Parcel in) {
+ return new CellBroadcastMessage(in);
+ }
+
+ public CellBroadcastMessage[] newArray(int size) {
+ return new CellBroadcastMessage[size];
+ }
+ };
+
+ /**
+ * Create a CellBroadcastMessage from a row in the database.
+ * @param cursor an open SQLite cursor pointing to the row to read
+ * @return the new CellBroadcastMessage
+ * @throws IllegalArgumentException if one of the required columns is missing
+ */
+ public static CellBroadcastMessage createFromCursor(Cursor cursor) {
+ int geoScope = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE));
+ int serialNum = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER));
+ int category = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY));
+ String language = cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE));
+ String body = cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY));
+ int format = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT));
+ int priority = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY));
+
+ String plmn;
+ int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN);
+ if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
+ plmn = cursor.getString(plmnColumn);
+ } else {
+ plmn = null;
+ }
+
+ int lac;
+ int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC);
+ if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
+ lac = cursor.getInt(lacColumn);
+ } else {
+ lac = -1;
+ }
+
+ int cid;
+ int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID);
+ if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
+ cid = cursor.getInt(cidColumn);
+ } else {
+ cid = -1;
+ }
+
+ SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
+
+ SmsCbEtwsInfo etwsInfo;
+ int etwsWarningTypeColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.ETWS_WARNING_TYPE);
+ if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
+ int warningType = cursor.getInt(etwsWarningTypeColumn);
+ etwsInfo = new SmsCbEtwsInfo(warningType, false, false, null);
+ } else {
+ etwsInfo = null;
+ }
+
+ SmsCbCmasInfo cmasInfo;
+ int cmasMessageClassColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS);
+ if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
+ int messageClass = cursor.getInt(cmasMessageClassColumn);
+
+ int cmasCategory;
+ int cmasCategoryColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_CATEGORY);
+ if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
+ cmasCategory = cursor.getInt(cmasCategoryColumn);
+ } else {
+ cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
+ }
+
+ int responseType;
+ int cmasResponseTypeColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE);
+ if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
+ responseType = cursor.getInt(cmasResponseTypeColumn);
+ } else {
+ responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
+ }
+
+ int severity;
+ int cmasSeverityColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_SEVERITY);
+ if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
+ severity = cursor.getInt(cmasSeverityColumn);
+ } else {
+ severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
+ }
+
+ int urgency;
+ int cmasUrgencyColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_URGENCY);
+ if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
+ urgency = cursor.getInt(cmasUrgencyColumn);
+ } else {
+ urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
+ }
+
+ int certainty;
+ int cmasCertaintyColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_CERTAINTY);
+ if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
+ certainty = cursor.getInt(cmasCertaintyColumn);
+ } else {
+ certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
+ }
+
+ cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
+ urgency, certainty);
+ } else {
+ cmasInfo = null;
+ }
+
+ SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category,
+ language, body, priority, etwsInfo, cmasInfo);
+
+ long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow(
+ Telephony.CellBroadcasts.DELIVERY_TIME));
+ boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.CellBroadcasts.MESSAGE_READ)) != 0);
+
+ return new CellBroadcastMessage(msg, deliveryTime, isRead);
+ }
+
+ /**
+ * Return a ContentValues object for insertion into the database.
+ * @return a new ContentValues object containing this object's data
+ */
+ public ContentValues getContentValues() {
+ ContentValues cv = new ContentValues(16);
+ SmsCbMessage msg = mSmsCbMessage;
+ cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope());
+ SmsCbLocation location = msg.getLocation();
+ if (location.getPlmn() != null) {
+ cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn());
+ }
+ if (location.getLac() != -1) {
+ cv.put(Telephony.CellBroadcasts.LAC, location.getLac());
+ }
+ if (location.getCid() != -1) {
+ cv.put(Telephony.CellBroadcasts.CID, location.getCid());
+ }
+ cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber());
+ cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory());
+ cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode());
+ cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody());
+ cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime);
+ cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead);
+ cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat());
+ cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority());
+
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ if (etwsInfo != null) {
+ cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+ }
+
+ SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
+ if (cmasInfo != null) {
+ cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
+ cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
+ cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
+ cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
+ cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
+ cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
+ }
+
+ return cv;
+ }
+
+ /**
+ * Set or clear the "read message" flag.
+ * @param isRead true if the message has been read; false if not
+ */
+ public void setIsRead(boolean isRead) {
+ mIsRead = isRead;
+ }
+
+ public String getLanguageCode() {
+ return mSmsCbMessage.getLanguageCode();
+ }
+
+ public int getServiceCategory() {
+ return mSmsCbMessage.getServiceCategory();
+ }
+
+ public long getDeliveryTime() {
+ return mDeliveryTime;
+ }
+
+ public String getMessageBody() {
+ return mSmsCbMessage.getMessageBody();
+ }
+
+ public boolean isRead() {
+ return mIsRead;
+ }
+
+ public int getSerialNumber() {
+ return mSmsCbMessage.getSerialNumber();
+ }
+
+ public SmsCbCmasInfo getCmasWarningInfo() {
+ return mSmsCbMessage.getCmasWarningInfo();
+ }
+
+ public SmsCbEtwsInfo getEtwsWarningInfo() {
+ return mSmsCbMessage.getEtwsWarningInfo();
+ }
+
+ /**
+ * Return whether the broadcast is an emergency (PWS) message type.
+ * This includes lower priority test messages and Amber alerts.
+ *
+ * All public alerts show the flashing warning icon in the dialog,
+ * but only emergency alerts play the alert sound and speak the message.
+ *
+ * @return true if the message is PWS type; false otherwise
+ */
+ public boolean isPublicAlertMessage() {
+ return mSmsCbMessage.isEmergencyMessage();
+ }
+
+ /**
+ * Returns whether the broadcast is an emergency (PWS) message type,
+ * including test messages, but excluding lower priority Amber alert broadcasts.
+ *
+ * @return true if the message is PWS type, excluding Amber alerts
+ */
+ public boolean isEmergencyAlertMessage() {
+ if (!mSmsCbMessage.isEmergencyMessage()) {
+ return false;
+ }
+ SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
+ if (cmasInfo != null &&
+ cmasInfo.getMessageClass() == SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS emergency message type.
+ * @return true if the message is ETWS emergency type; false otherwise
+ */
+ public boolean isEtwsMessage() {
+ return mSmsCbMessage.isEtwsMessage();
+ }
+
+ /**
+ * Return whether the broadcast is a CMAS emergency message type.
+ * @return true if the message is CMAS emergency type; false otherwise
+ */
+ public boolean isCmasMessage() {
+ return mSmsCbMessage.isCmasMessage();
+ }
+
+ /**
+ * Return the CMAS message class.
+ * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or
+ * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert
+ */
+ public int getCmasMessageClass() {
+ if (mSmsCbMessage.isCmasMessage()) {
+ return mSmsCbMessage.getCmasWarningInfo().getMessageClass();
+ } else {
+ return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
+ }
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS popup alert.
+ * This method checks the message ID and the message code.
+ * @return true if the message indicates an ETWS popup alert
+ */
+ public boolean isEtwsPopupAlert() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null && etwsInfo.isPopupAlert();
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS emergency user alert.
+ * This method checks the message ID and the message code.
+ * @return true if the message indicates an ETWS emergency user alert
+ */
+ public boolean isEtwsEmergencyUserAlert() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null && etwsInfo.isEmergencyUserAlert();
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS test message.
+ * @return true if the message is an ETWS test message; false otherwise
+ */
+ public boolean isEtwsTestMessage() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null &&
+ etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
+ }
+
+ /**
+ * Return the abbreviated date string for the message delivery time.
+ * @param context the context object
+ * @return a String to use in the broadcast list UI
+ */
+ public String getDateString(Context context) {
+ int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME |
+ DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE |
+ DateUtils.FORMAT_CAP_AMPM;
+ return DateUtils.formatDateTime(context, mDeliveryTime, flags);
+ }
+
+ /**
+ * Return the date string for the message delivery time, suitable for text-to-speech.
+ * @param context the context object
+ * @return a String for populating the list item AccessibilityEvent for TTS
+ */
+ public String getSpokenDateString(Context context) {
+ int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
+ return DateUtils.formatDateTime(context, mDeliveryTime, flags);
+ }
+}
diff --git a/src/java/android/telephony/SmsCbCmasInfo.java b/src/java/android/telephony/SmsCbCmasInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a89d94ab2ef56237c7f093eb2b8f64a67ac2cf5
--- /dev/null
+++ b/src/java/android/telephony/SmsCbCmasInfo.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains CMAS warning notification Type 1 elements for a {@link SmsCbMessage}.
+ * Supported values for each element are defined in TIA-1149-0-1 (CMAS over CDMA) and
+ * 3GPP TS 23.041 (for GSM/UMTS).
+ *
+ * {@hide}
+ */
+public class SmsCbCmasInfo implements Parcelable {
+
+ // CMAS message class (in GSM/UMTS message identifier or CDMA service category).
+
+ /** Presidential-level alert (Korean Public Alert System Class 0 message). */
+ public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
+
+ /** Extreme threat to life and property (Korean Public Alert System Class 1 message). */
+ public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
+
+ /** Severe threat to life and property (Korean Public Alert System Class 1 message). */
+ public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
+
+ /** Child abduction emergency (AMBER Alert). */
+ public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03;
+
+ /** CMAS test message. */
+ public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
+
+ /** CMAS exercise. */
+ public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
+
+ /** CMAS category for operator defined use. */
+ public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 0x06;
+
+ /** CMAS category for warning types that are reserved for future extension. */
+ public static final int CMAS_CLASS_UNKNOWN = -1;
+
+ // CMAS alert category (in CDMA type 1 elements record).
+
+ /** CMAS alert category: Geophysical including landslide. */
+ public static final int CMAS_CATEGORY_GEO = 0x00;
+
+ /** CMAS alert category: Meteorological including flood. */
+ public static final int CMAS_CATEGORY_MET = 0x01;
+
+ /** CMAS alert category: General emergency and public safety. */
+ public static final int CMAS_CATEGORY_SAFETY = 0x02;
+
+ /** CMAS alert category: Law enforcement, military, homeland/local/private security. */
+ public static final int CMAS_CATEGORY_SECURITY = 0x03;
+
+ /** CMAS alert category: Rescue and recovery. */
+ public static final int CMAS_CATEGORY_RESCUE = 0x04;
+
+ /** CMAS alert category: Fire suppression and rescue. */
+ public static final int CMAS_CATEGORY_FIRE = 0x05;
+
+ /** CMAS alert category: Medical and public health. */
+ public static final int CMAS_CATEGORY_HEALTH = 0x06;
+
+ /** CMAS alert category: Pollution and other environmental. */
+ public static final int CMAS_CATEGORY_ENV = 0x07;
+
+ /** CMAS alert category: Public and private transportation. */
+ public static final int CMAS_CATEGORY_TRANSPORT = 0x08;
+
+ /** CMAS alert category: Utility, telecom, other non-transport infrastructure. */
+ public static final int CMAS_CATEGORY_INFRA = 0x09;
+
+ /** CMAS alert category: Chem, bio, radiological, nuclear, high explosive threat or attack. */
+ public static final int CMAS_CATEGORY_CBRNE = 0x0a;
+
+ /** CMAS alert category: Other events. */
+ public static final int CMAS_CATEGORY_OTHER = 0x0b;
+
+ /**
+ * CMAS alert category is unknown. The category is only available for CDMA broadcasts
+ * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+ */
+ public static final int CMAS_CATEGORY_UNKNOWN = -1;
+
+ // CMAS response type (in CDMA type 1 elements record).
+
+ /** CMAS response type: Take shelter in place. */
+ public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00;
+
+ /** CMAS response type: Evacuate (Relocate). */
+ public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01;
+
+ /** CMAS response type: Make preparations. */
+ public static final int CMAS_RESPONSE_TYPE_PREPARE = 0x02;
+
+ /** CMAS response type: Execute a pre-planned activity. */
+ public static final int CMAS_RESPONSE_TYPE_EXECUTE = 0x03;
+
+ /** CMAS response type: Attend to information sources. */
+ public static final int CMAS_RESPONSE_TYPE_MONITOR = 0x04;
+
+ /** CMAS response type: Avoid hazard. */
+ public static final int CMAS_RESPONSE_TYPE_AVOID = 0x05;
+
+ /** CMAS response type: Evaluate the information in this message (not for public warnings). */
+ public static final int CMAS_RESPONSE_TYPE_ASSESS = 0x06;
+
+ /** CMAS response type: No action recommended. */
+ public static final int CMAS_RESPONSE_TYPE_NONE = 0x07;
+
+ /**
+ * CMAS response type is unknown. The response type is only available for CDMA broadcasts
+ * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+ */
+ public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1;
+
+ // 4-bit CMAS severity (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS severity type: Extraordinary threat to life or property. */
+ public static final int CMAS_SEVERITY_EXTREME = 0x0;
+
+ /** CMAS severity type: Significant threat to life or property. */
+ public static final int CMAS_SEVERITY_SEVERE = 0x1;
+
+ /**
+ * CMAS alert severity is unknown. The severity is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_SEVERITY_UNKNOWN = -1;
+
+ // CMAS urgency (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS urgency type: Responsive action should be taken immediately. */
+ public static final int CMAS_URGENCY_IMMEDIATE = 0x0;
+
+ /** CMAS urgency type: Responsive action should be taken within the next hour. */
+ public static final int CMAS_URGENCY_EXPECTED = 0x1;
+
+ /**
+ * CMAS alert urgency is unknown. The urgency is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_URGENCY_UNKNOWN = -1;
+
+ // CMAS certainty (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS certainty type: Determined to have occurred or to be ongoing. */
+ public static final int CMAS_CERTAINTY_OBSERVED = 0x0;
+
+ /** CMAS certainty type: Likely (probability > ~50%). */
+ public static final int CMAS_CERTAINTY_LIKELY = 0x1;
+
+ /**
+ * CMAS alert certainty is unknown. The certainty is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_CERTAINTY_UNKNOWN = -1;
+
+ /** CMAS message class. */
+ private final int mMessageClass;
+
+ /** CMAS category. */
+ private final int mCategory;
+
+ /** CMAS response type. */
+ private final int mResponseType;
+
+ /** CMAS severity. */
+ private final int mSeverity;
+
+ /** CMAS urgency. */
+ private final int mUrgency;
+
+ /** CMAS certainty. */
+ private final int mCertainty;
+
+ /** Create a new SmsCbCmasInfo object with the specified values. */
+ public SmsCbCmasInfo(int messageClass, int category, int responseType, int severity,
+ int urgency, int certainty) {
+ mMessageClass = messageClass;
+ mCategory = category;
+ mResponseType = responseType;
+ mSeverity = severity;
+ mUrgency = urgency;
+ mCertainty = certainty;
+ }
+
+ /** Create a new SmsCbCmasInfo object from a Parcel. */
+ SmsCbCmasInfo(Parcel in) {
+ mMessageClass = in.readInt();
+ mCategory = in.readInt();
+ mResponseType = in.readInt();
+ mSeverity = in.readInt();
+ mUrgency = in.readInt();
+ mCertainty = in.readInt();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageClass);
+ dest.writeInt(mCategory);
+ dest.writeInt(mResponseType);
+ dest.writeInt(mSeverity);
+ dest.writeInt(mUrgency);
+ dest.writeInt(mCertainty);
+ }
+
+ /**
+ * Returns the CMAS message class, e.g. {@link #CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT}.
+ * @return one of the {@code CMAS_CLASS} values
+ */
+ public int getMessageClass() {
+ return mMessageClass;
+ }
+
+ /**
+ * Returns the CMAS category, e.g. {@link #CMAS_CATEGORY_GEO}.
+ * @return one of the {@code CMAS_CATEGORY} values
+ */
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
+ * Returns the CMAS response type, e.g. {@link #CMAS_RESPONSE_TYPE_SHELTER}.
+ * @return one of the {@code CMAS_RESPONSE_TYPE} values
+ */
+ public int getResponseType() {
+ return mResponseType;
+ }
+
+ /**
+ * Returns the CMAS severity, e.g. {@link #CMAS_SEVERITY_EXTREME}.
+ * @return one of the {@code CMAS_SEVERITY} values
+ */
+ public int getSeverity() {
+ return mSeverity;
+ }
+
+ /**
+ * Returns the CMAS urgency, e.g. {@link #CMAS_URGENCY_IMMEDIATE}.
+ * @return one of the {@code CMAS_URGENCY} values
+ */
+ public int getUrgency() {
+ return mUrgency;
+ }
+
+ /**
+ * Returns the CMAS certainty, e.g. {@link #CMAS_CERTAINTY_OBSERVED}.
+ * @return one of the {@code CMAS_CERTAINTY} values
+ */
+ public int getCertainty() {
+ return mCertainty;
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbCmasInfo{messageClass=" + mMessageClass + ", category=" + mCategory
+ + ", responseType=" + mResponseType + ", severity=" + mSeverity
+ + ", urgency=" + mUrgency + ", certainty=" + mCertainty + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Parcelable.Creator
+ CREATOR = new Parcelable.Creator() {
+ public SmsCbCmasInfo createFromParcel(Parcel in) {
+ return new SmsCbCmasInfo(in);
+ }
+
+ public SmsCbCmasInfo[] newArray(int size) {
+ return new SmsCbCmasInfo[size];
+ }
+ };
+}
diff --git a/src/java/android/telephony/SmsCbEtwsInfo.java b/src/java/android/telephony/SmsCbEtwsInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..0890d528f866cd3f149d90f77335afc8a5135cc4
--- /dev/null
+++ b/src/java/android/telephony/SmsCbEtwsInfo.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.Time;
+
+import com.android.internal.telephony.IccUtils;
+
+import java.util.Arrays;
+
+/**
+ * Contains information elements for a GSM or UMTS ETWS warning notification.
+ * Supported values for each element are defined in 3GPP TS 23.041.
+ *
+ * {@hide}
+ */
+public class SmsCbEtwsInfo implements Parcelable {
+
+ /** ETWS warning type for earthquake. */
+ public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
+
+ /** ETWS warning type for tsunami. */
+ public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
+
+ /** ETWS warning type for earthquake and tsunami. */
+ public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
+
+ /** ETWS warning type for test messages. */
+ public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
+
+ /** ETWS warning type for other emergency types. */
+ public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
+
+ /** Unknown ETWS warning type. */
+ public static final int ETWS_WARNING_TYPE_UNKNOWN = -1;
+
+ /** One of the ETWS warning type constants defined in this class. */
+ private final int mWarningType;
+
+ /** Whether or not to activate the emergency user alert tone and vibration. */
+ private final boolean mEmergencyUserAlert;
+
+ /** Whether or not to activate a popup alert. */
+ private final boolean mActivatePopup;
+
+ /**
+ * 50-byte security information (ETWS primary notification for GSM only). As of Release 10,
+ * 3GPP TS 23.041 states that the UE shall ignore the ETWS primary notification timestamp
+ * and digital signature if received. Therefore it is treated as a raw byte array and
+ * parceled with the broadcast intent if present, but the timestamp is only computed if an
+ * application asks for the individual components.
+ */
+ private final byte[] mWarningSecurityInformation;
+
+ /** Create a new SmsCbEtwsInfo object with the specified values. */
+ public SmsCbEtwsInfo(int warningType, boolean emergencyUserAlert, boolean activatePopup,
+ byte[] warningSecurityInformation) {
+ mWarningType = warningType;
+ mEmergencyUserAlert = emergencyUserAlert;
+ mActivatePopup = activatePopup;
+ mWarningSecurityInformation = warningSecurityInformation;
+ }
+
+ /** Create a new SmsCbEtwsInfo object from a Parcel. */
+ SmsCbEtwsInfo(Parcel in) {
+ mWarningType = in.readInt();
+ mEmergencyUserAlert = (in.readInt() != 0);
+ mActivatePopup = (in.readInt() != 0);
+ mWarningSecurityInformation = in.createByteArray();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWarningType);
+ dest.writeInt(mEmergencyUserAlert ? 1 : 0);
+ dest.writeInt(mActivatePopup ? 1 : 0);
+ dest.writeByteArray(mWarningSecurityInformation);
+ }
+
+ /**
+ * Returns the ETWS warning type.
+ * @return a warning type such as {@link #ETWS_WARNING_TYPE_EARTHQUAKE}
+ */
+ public int getWarningType() {
+ return mWarningType;
+ }
+
+ /**
+ * Returns the ETWS emergency user alert flag.
+ * @return true to notify terminal to activate emergency user alert; false otherwise
+ */
+ public boolean isEmergencyUserAlert() {
+ return mEmergencyUserAlert;
+ }
+
+ /**
+ * Returns the ETWS activate popup flag.
+ * @return true to notify terminal to activate display popup; false otherwise
+ */
+ public boolean isPopupAlert() {
+ return mActivatePopup;
+ }
+
+ /**
+ * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
+ * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
+ * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
+ */
+ public long getPrimaryNotificationTimestamp() {
+ if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
+ return 0;
+ }
+
+ int year = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[0]);
+ int month = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[1]);
+ int day = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[2]);
+ int hour = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[3]);
+ int minute = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[4]);
+ int second = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[5]);
+
+ // For the timezone, the most significant bit of the
+ // least significant nibble is the sign byte
+ // (meaning the max range of this field is 79 quarter-hours,
+ // which is more than enough)
+
+ byte tzByte = mWarningSecurityInformation[6];
+
+ // Mask out sign bit.
+ int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
+
+ timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
+
+ Time time = new Time(Time.TIMEZONE_UTC);
+
+ // We only need to support years above 2000.
+ time.year = year + 2000;
+ time.month = month - 1;
+ time.monthDay = day;
+ time.hour = hour;
+ time.minute = minute;
+ time.second = second;
+
+ // Timezone offset is in quarter hours.
+ return time.toMillis(true) - (long) (timezoneOffset * 15 * 60 * 1000);
+ }
+
+ /**
+ * Returns the digital signature (GSM primary notifications only). As of Release 10,
+ * 3GPP TS 23.041 states that the UE shall ignore this value if received.
+ * @return a byte array containing a copy of the primary notification digital signature
+ */
+ public byte[] getPrimaryNotificationSignature() {
+ if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 50) {
+ return null;
+ }
+ return Arrays.copyOfRange(mWarningSecurityInformation, 7, 50);
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbEtwsInfo{warningType=" + mWarningType + ", emergencyUserAlert="
+ + mEmergencyUserAlert + ", activatePopup=" + mActivatePopup + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Creator CREATOR = new Creator() {
+ public SmsCbEtwsInfo createFromParcel(Parcel in) {
+ return new SmsCbEtwsInfo(in);
+ }
+
+ public SmsCbEtwsInfo[] newArray(int size) {
+ return new SmsCbEtwsInfo[size];
+ }
+ };
+}
diff --git a/src/java/android/telephony/SmsCbLocation.java b/src/java/android/telephony/SmsCbLocation.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b5bd0d4bb71f8d72daf2ce83274e14e5ca0550f
--- /dev/null
+++ b/src/java/android/telephony/SmsCbLocation.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.gsm.GsmCellLocation;
+
+/**
+ * Represents the location and geographical scope of a cell broadcast message.
+ * For GSM/UMTS, the Location Area and Cell ID are set when the broadcast
+ * geographical scope is cell wide or Location Area wide. For CDMA, the
+ * broadcast geographical scope is always PLMN wide.
+ *
+ * @hide
+ */
+public class SmsCbLocation implements Parcelable {
+
+ /** The PLMN. Note that this field may be an empty string, but isn't allowed to be null. */
+ private final String mPlmn;
+
+ private final int mLac;
+ private final int mCid;
+
+ /**
+ * Construct an empty location object. This is used for some test cases, and for
+ * cell broadcasts saved in older versions of the database without location info.
+ */
+ public SmsCbLocation() {
+ mPlmn = "";
+ mLac = -1;
+ mCid = -1;
+ }
+
+ /**
+ * Construct a location object for the PLMN. This class is immutable, so
+ * the same object can be reused for multiple broadcasts.
+ */
+ public SmsCbLocation(String plmn) {
+ mPlmn = plmn;
+ mLac = -1;
+ mCid = -1;
+ }
+
+ /**
+ * Construct a location object for the PLMN, LAC, and Cell ID. This class is immutable, so
+ * the same object can be reused for multiple broadcasts.
+ */
+ public SmsCbLocation(String plmn, int lac, int cid) {
+ mPlmn = plmn;
+ mLac = lac;
+ mCid = cid;
+ }
+
+ /**
+ * Initialize the object from a Parcel.
+ */
+ public SmsCbLocation(Parcel in) {
+ mPlmn = in.readString();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ }
+
+ /**
+ * Returns the MCC/MNC of the network as a String.
+ * @return the PLMN identifier (MCC+MNC) as a String
+ */
+ public String getPlmn() {
+ return mPlmn;
+ }
+
+ /**
+ * Returns the GSM location area code, or UMTS service area code.
+ * @return location area code, -1 if unknown, 0xffff max legal value
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * Returns the GSM or UMTS cell ID.
+ * @return gsm cell id, -1 if unknown, 0xffff max legal value
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mPlmn.hashCode();
+ hash = hash * 31 + mLac;
+ hash = hash * 31 + mCid;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || !(o instanceof SmsCbLocation)) {
+ return false;
+ }
+ SmsCbLocation other = (SmsCbLocation) o;
+ return mPlmn.equals(other.mPlmn) && mLac == other.mLac && mCid == other.mCid;
+ }
+
+ @Override
+ public String toString() {
+ return '[' + mPlmn + ',' + mLac + ',' + mCid + ']';
+ }
+
+ /**
+ * Test whether this location is within the location area of the specified object.
+ *
+ * @param area the location area to compare with this location
+ * @return true if this location is contained within the specified location area
+ */
+ public boolean isInLocationArea(SmsCbLocation area) {
+ if (mCid != -1 && mCid != area.mCid) {
+ return false;
+ }
+ if (mLac != -1 && mLac != area.mLac) {
+ return false;
+ }
+ return mPlmn.equals(area.mPlmn);
+ }
+
+ /**
+ * Test whether this location is within the location area of the CellLocation.
+ *
+ * @param plmn the PLMN to use for comparison
+ * @param lac the Location Area (GSM) or Service Area (UMTS) to compare with
+ * @param cid the Cell ID to compare with
+ * @return true if this location is contained within the specified PLMN, LAC, and Cell ID
+ */
+ public boolean isInLocationArea(String plmn, int lac, int cid) {
+ if (!mPlmn.equals(plmn)) {
+ return false;
+ }
+
+ if (mLac != -1 && mLac != lac) {
+ return false;
+ }
+
+ if (mCid != -1 && mCid != cid) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPlmn);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ @Override
+ public SmsCbLocation createFromParcel(Parcel in) {
+ return new SmsCbLocation(in);
+ }
+
+ @Override
+ public SmsCbLocation[] newArray(int size) {
+ return new SmsCbLocation[size];
+ }
+ };
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/src/java/android/telephony/SmsCbMessage.java b/src/java/android/telephony/SmsCbMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..046bf8c700eb48453063af4e42e9f91121818fac
--- /dev/null
+++ b/src/java/android/telephony/SmsCbMessage.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object containing a received cell broadcast message. There are four different types
+ * of Cell Broadcast messages:
+ *
+ *
+ * - opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores
+ * - cell information messages, broadcast on channel 50, indicating the current cell name for
+ * roaming purposes (required to display on the idle screen in Brazil)
+ * - emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)
+ * - emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)
+ *
+ *
+ * There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
+ * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
+ * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
+ * two completely different concepts in 3GPP and CDMA.
+ *
+ *
The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
+ * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
+ * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
+ * application should
+ *
+ *
The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
+ * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
+ * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
+ * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
+ * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
+ * Service Area in UMTS). The relevant values are concatenated into a single String which will be
+ * unique if the messages are not duplicates.
+ *
+ *
The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
+ * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
+ *
+ *
Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
+ * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
+ * Only system applications such as the CellBroadcastReceiver may receive notifications for
+ * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
+ * interference with the immediate display of the alert message and playing of the alert sound and
+ * vibration pattern, which could be caused by poorly written or malicious non-system code.
+ *
+ * @hide
+ */
+public class SmsCbMessage implements Parcelable {
+
+ protected static final String LOG_TAG = "SMSCB";
+
+ /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+ /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
+ public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+ /** Location / service area wide geographical scope (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+ /** Cell wide geographical scope (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+ /** GSM or UMTS format cell broadcast. */
+ public static final int MESSAGE_FORMAT_3GPP = 1;
+
+ /** CDMA format cell broadcast. */
+ public static final int MESSAGE_FORMAT_3GPP2 = 2;
+
+ /** Normal message priority. */
+ public static final int MESSAGE_PRIORITY_NORMAL = 0;
+
+ /** Interactive message priority. */
+ public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
+
+ /** Urgent message priority. */
+ public static final int MESSAGE_PRIORITY_URGENT = 2;
+
+ /** Emergency message priority. */
+ public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
+
+ /** Format of this message (for interpretation of service category values). */
+ private final int mMessageFormat;
+
+ /** Geographical scope of broadcast. */
+ private final int mGeographicalScope;
+
+ /**
+ * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
+ * update number for GSM/UMTS). The serial number plus the location code uniquely identify
+ * a cell broadcast for duplicate detection.
+ */
+ private final int mSerialNumber;
+
+ /**
+ * Location identifier for this message. It consists of the current operator MCC/MNC as a
+ * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+ * message is not binary 01, the Location Area is included for comparison. If the GS is
+ * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
+ */
+ private final SmsCbLocation mLocation;
+
+ /**
+ * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
+ * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
+ * or {@link #getCmasWarningInfo()}.
+ */
+ private final int mServiceCategory;
+
+ /** Message language, as a two-character string, e.g. "en". */
+ private final String mLanguage;
+
+ /** Message body, as a String. */
+ private final String mBody;
+
+ /** Message priority (including emergency priority). */
+ private final int mPriority;
+
+ /** ETWS warning notification information (ETWS warnings only). */
+ private final SmsCbEtwsInfo mEtwsWarningInfo;
+
+ /** CMAS warning notification information (CMAS warnings only). */
+ private final SmsCbCmasInfo mCmasWarningInfo;
+
+ /**
+ * Create a new SmsCbMessage with the specified data.
+ */
+ public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+ SmsCbLocation location, int serviceCategory, String language, String body,
+ int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
+ mMessageFormat = messageFormat;
+ mGeographicalScope = geographicalScope;
+ mSerialNumber = serialNumber;
+ mLocation = location;
+ mServiceCategory = serviceCategory;
+ mLanguage = language;
+ mBody = body;
+ mPriority = priority;
+ mEtwsWarningInfo = etwsWarningInfo;
+ mCmasWarningInfo = cmasWarningInfo;
+ }
+
+ /** Create a new SmsCbMessage object from a Parcel. */
+ public SmsCbMessage(Parcel in) {
+ mMessageFormat = in.readInt();
+ mGeographicalScope = in.readInt();
+ mSerialNumber = in.readInt();
+ mLocation = new SmsCbLocation(in);
+ mServiceCategory = in.readInt();
+ mLanguage = in.readString();
+ mBody = in.readString();
+ mPriority = in.readInt();
+ int type = in.readInt();
+ switch (type) {
+ case 'E':
+ // unparcel ETWS warning information
+ mEtwsWarningInfo = new SmsCbEtwsInfo(in);
+ mCmasWarningInfo = null;
+ break;
+
+ case 'C':
+ // unparcel CMAS warning information
+ mEtwsWarningInfo = null;
+ mCmasWarningInfo = new SmsCbCmasInfo(in);
+ break;
+
+ default:
+ mEtwsWarningInfo = null;
+ mCmasWarningInfo = null;
+ }
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageFormat);
+ dest.writeInt(mGeographicalScope);
+ dest.writeInt(mSerialNumber);
+ mLocation.writeToParcel(dest, flags);
+ dest.writeInt(mServiceCategory);
+ dest.writeString(mLanguage);
+ dest.writeString(mBody);
+ dest.writeInt(mPriority);
+ if (mEtwsWarningInfo != null) {
+ // parcel ETWS warning information
+ dest.writeInt('E');
+ mEtwsWarningInfo.writeToParcel(dest, flags);
+ } else if (mCmasWarningInfo != null) {
+ // parcel CMAS warning information
+ dest.writeInt('C');
+ mCmasWarningInfo.writeToParcel(dest, flags);
+ } else {
+ // no ETWS or CMAS warning information
+ dest.writeInt('0');
+ }
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ @Override
+ public SmsCbMessage createFromParcel(Parcel in) {
+ return new SmsCbMessage(in);
+ }
+
+ @Override
+ public SmsCbMessage[] newArray(int size) {
+ return new SmsCbMessage[size];
+ }
+ };
+
+ /**
+ * Return the geographical scope of this message (GSM/UMTS only).
+ *
+ * @return Geographical scope
+ */
+ public int getGeographicalScope() {
+ return mGeographicalScope;
+ }
+
+ /**
+ * Return the broadcast serial number of broadcast (message identifier for CDMA, or
+ * geographical scope + message code + update number for GSM/UMTS). The serial number plus
+ * the location code uniquely identify a cell broadcast for duplicate detection.
+ *
+ * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
+ */
+ public int getSerialNumber() {
+ return mSerialNumber;
+ }
+
+ /**
+ * Return the location identifier for this message, consisting of the MCC/MNC as a
+ * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+ * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
+ * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
+ * if the location is included within another location area or within a PLMN and CellLocation.
+ *
+ * @return the geographical location code for duplicate message detection
+ */
+ public SmsCbLocation getLocation() {
+ return mLocation;
+ }
+
+ /**
+ * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
+ * of the category is radio technology specific. For ETWS and CMAS warnings, the information
+ * provided by the category is available via {@link #getEtwsWarningInfo()} or
+ * {@link #getCmasWarningInfo()} in a radio technology independent format.
+ *
+ * @return the radio technology specific service category
+ */
+ public int getServiceCategory() {
+ return mServiceCategory;
+ }
+
+ /**
+ * Get the ISO-639-1 language code for this message, or null if unspecified
+ *
+ * @return Language code
+ */
+ public String getLanguageCode() {
+ return mLanguage;
+ }
+
+ /**
+ * Get the body of this message, or null if no body available
+ *
+ * @return Body, or null
+ */
+ public String getMessageBody() {
+ return mBody;
+ }
+
+ /**
+ * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
+ * @return an integer representing 3GPP or 3GPP2 message format
+ */
+ public int getMessageFormat() {
+ return mMessageFormat;
+ }
+
+ /**
+ * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
+ * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
+ * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
+ * @return an integer representing the message priority
+ */
+ public int getMessagePriority() {
+ return mPriority;
+ }
+
+ /**
+ * If this is an ETWS warning notification then this method will return an object containing
+ * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
+ * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
+ * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
+ * ETWS primary notification timestamp and digital signature if received.
+ *
+ * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
+ */
+ public SmsCbEtwsInfo getEtwsWarningInfo() {
+ return mEtwsWarningInfo;
+ }
+
+ /**
+ * If this is a CMAS warning notification then this method will return an object containing
+ * the CMAS message class, category, response type, severity, urgency and certainty.
+ * The message class is always present. Severity, urgency and certainty are present for CDMA
+ * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
+ * except for the Presidential-level alert category. Category and response type are only
+ * available for CDMA notifications containing a type 1 elements record.
+ *
+ * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
+ */
+ public SmsCbCmasInfo getCmasWarningInfo() {
+ return mCmasWarningInfo;
+ }
+
+ /**
+ * Return whether this message is an emergency (PWS) message type.
+ * @return true if the message is a public warning notification; false otherwise
+ */
+ public boolean isEmergencyMessage() {
+ return mPriority == MESSAGE_PRIORITY_EMERGENCY;
+ }
+
+ /**
+ * Return whether this message is an ETWS warning alert.
+ * @return true if the message is an ETWS warning notification; false otherwise
+ */
+ public boolean isEtwsMessage() {
+ return mEtwsWarningInfo != null;
+ }
+
+ /**
+ * Return whether this message is a CMAS warning alert.
+ * @return true if the message is a CMAS warning notification; false otherwise
+ */
+ public boolean isCmasMessage() {
+ return mCmasWarningInfo != null;
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
+ + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
+ + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
+ + ", priority=" + mPriority
+ + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
+ + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..44bdaeb7876e649fb70e6407a68f9902ff5a9f46
--- /dev/null
+++ b/src/java/android/telephony/SmsManager.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.app.PendingIntent;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.IccConstants;
+import com.android.internal.telephony.SmsRawData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state? Presumably this cannot be changed without
+ * interfering with the API...
+ */
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ */
+public final class SmsManager {
+ /** Singleton object constructed during class initialization. */
+ private static final SmsManager sInstance = new SmsManager();
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK
for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ */
+ public void sendTextMessage(
+ 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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Divide a message text into several fragments, none bigger than
+ * the maximum SMS message size.
+ *
+ * @param text the original message. Must not be null.
+ * @return an ArrayList
of strings that, in order,
+ * comprise the original message
+ */
+ public ArrayList divideMessage(String text) {
+ return SmsMessage.fragmentText(text);
+ }
+
+ /**
+ * Send a multi-part text based SMS. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * divideMessage
.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be Activity.RESULT_OK
for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ public void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList parts,
+ ArrayList sentIntents, ArrayList deliveryIntents) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+ if (parts == null || parts.size() < 1) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (parts.size() > 1) {
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.sendMultipartText(destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ } else {
+ PendingIntent sentIntent = null;
+ PendingIntent deliveryIntent = null;
+ if (sentIntents != null && sentIntents.size() > 0) {
+ sentIntent = sentIntents.get(0);
+ }
+ if (deliveryIntents != null && deliveryIntents.size() > 0) {
+ deliveryIntent = deliveryIntents.get(0);
+ }
+ sendTextMessage(destinationAddress, scAddress, parts.get(0),
+ sentIntent, deliveryIntent);
+ }
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param destinationPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK
for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ public void sendDataMessage(
+ 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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.sendData(destinationAddress, scAddress, destinationPort & 0xFFFF,
+ data, sentIntent, deliveryIntent);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Get the default instance of the SmsManager
+ *
+ * @return the default instance of the SmsManager
+ */
+ public static SmsManager getDefault() {
+ return sInstance;
+ }
+
+ private SmsManager() {
+ //nothing
+ }
+
+ /**
+ * Copy a raw SMS PDU to the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param smsc the SMSC for this message, or NULL for the default SMSC
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
+ * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
+ * @return true for success
+ *
+ * {@hide}
+ */
+ public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.copyMessageToIccEf(status, pdu, smsc);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Delete the specified message from the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex is the record index of the message on ICC
+ * @return true for success
+ *
+ * {@hide}
+ */
+ public boolean
+ deleteMessageFromIcc(int messageIndex) {
+ boolean success = false;
+ byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
+ Arrays.fill(pdu, (byte)0xff);
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.updateMessageOnIccEf(messageIndex, STATUS_ON_ICC_FREE, pdu);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Update the specified message on the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_ICC_READ,
+ * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+ * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+ * @param pdu the raw PDU to store
+ * @return true for success
+ *
+ * {@hide}
+ */
+ public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.updateMessageOnIccEf(messageIndex, newStatus, pdu);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Retrieves all messages currently stored on ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @return ArrayList
of SmsMessage
objects
+ *
+ * {@hide}
+ */
+ public static ArrayList getAllMessagesFromIcc() {
+ List records = null;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ records = iccISms.getAllMessagesFromIccEf();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return createMessageListFromRawRecords(records);
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcast(messageIdentifier);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcastRange(int, int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Create a list of SmsMessage
s from a list of RawSmsData
+ * records returned by getAllMessagesFromIcc()
+ *
+ * @param records SMS EF records, returned by
+ * getAllMessagesFromIcc
+ * @return ArrayList
of SmsMessage
objects.
+ */
+ private static ArrayList createMessageListFromRawRecords(List records) {
+ ArrayList messages = new ArrayList();
+ if (records != null) {
+ int count = records.size();
+ for (int i = 0; i < count; i++) {
+ SmsRawData data = records.get(i);
+ // List contains all records, including "free" records (null)
+ if (data != null) {
+ SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+ if (sms != null) {
+ messages.add(sms);
+ }
+ }
+ }
+ }
+ return messages;
+ }
+
+ // see SmsMessage.getStatusOnIcc
+
+ /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_FREE = 0;
+
+ /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_READ = 1;
+
+ /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_UNREAD = 3;
+
+ /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_SENT = 5;
+
+ /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_UNSENT = 7;
+
+ // SMS send failure result codes
+
+ /** Generic failure cause */
+ static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
+ /** Failed because radio was explicitly turned off */
+ static public final int RESULT_ERROR_RADIO_OFF = 2;
+ /** Failed because no pdu provided */
+ static public final int RESULT_ERROR_NULL_PDU = 3;
+ /** Failed because service is currently unavailable */
+ static public final int RESULT_ERROR_NO_SERVICE = 4;
+ /** Failed because we reached the sending queue limit. {@hide} */
+ static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
+ /** Failed because FDN is enabled. {@hide} */
+ static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6;
+}
diff --git a/src/java/android/telephony/SmsMessage.java b/src/java/android/telephony/SmsMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..b94609e493dd33c22b380e793b61c1c6b70dc55e
--- /dev/null
+++ b/src/java/android/telephony/SmsMessage.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.util.Log;
+
+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.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ */
+public class SmsMessage {
+ private static final String LOG_TAG = "SMS";
+
+ /**
+ * SMS Class enumeration.
+ * See TS 23.038.
+ *
+ */
+ public enum MessageClass{
+ UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+ }
+
+ /** User data text encoding code unit size */
+ public static final int ENCODING_UNKNOWN = 0;
+ public static final int ENCODING_7BIT = 1;
+ public static final int ENCODING_8BIT = 2;
+ public static final int ENCODING_16BIT = 3;
+ /**
+ * @hide This value is not defined in global standard. Only in Korea, this is used.
+ */
+ public static final int ENCODING_KSC5601 = 4;
+
+ /** The maximum number of payload bytes per message */
+ public static final int MAX_USER_DATA_BYTES = 140;
+
+ /**
+ * The maximum number of payload bytes per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ */
+ public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+ /** The maximum number of payload septets per message */
+ public static final int MAX_USER_DATA_SEPTETS = 160;
+
+ /**
+ * The maximum number of payload septets per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ */
+ public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+ /**
+ * Indicates a 3GPP format SMS message.
+ * @hide pending API council approval
+ */
+ public static final String FORMAT_3GPP = "3gpp";
+
+ /**
+ * Indicates a 3GPP2 format SMS message.
+ * @hide pending API council approval
+ */
+ public static final String FORMAT_3GPP2 = "3gpp2";
+
+ /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+ *
+ * @hide
+ */
+ public SmsMessageBase mWrappedSmsMessage;
+
+ public static class SubmitPdu {
+
+ public byte[] encodedScAddress; // Null if not applicable.
+ public byte[] encodedMessage;
+
+ public String toString() {
+ return "SubmitPdu: encodedScAddress = "
+ + Arrays.toString(encodedScAddress)
+ + ", encodedMessage = "
+ + Arrays.toString(encodedMessage);
+ }
+
+ /**
+ * @hide
+ */
+ protected SubmitPdu(SubmitPduBase spb) {
+ this.encodedMessage = spb.encodedMessage;
+ this.encodedScAddress = spb.encodedScAddress;
+ }
+
+ }
+
+ private SmsMessage(SmsMessageBase smb) {
+ mWrappedSmsMessage = smb;
+ }
+
+ /**
+ * Create an SmsMessage from a raw PDU.
+ *
+ * This method will soon be deprecated and all applications which handle
+ * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+ * intent must 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.
+ */
+ public static SmsMessage createFromPdu(byte[] pdu) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ String format = (PHONE_TYPE_CDMA == activePhone) ?
+ SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
+ return createFromPdu(pdu, format);
+ }
+
+ /**
+ * 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}
+ * 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
+ */
+ public static SmsMessage createFromPdu(byte[] pdu, String format) {
+ SmsMessageBase wrappedMessage;
+
+ if (SmsConstants.FORMAT_3GPP2.equals(format)) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+ } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+ } else {
+ Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+ return null;
+ }
+
+ return new SmsMessage(wrappedMessage);
+ }
+
+ /**
+ * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
+ * +CMT unsolicited response (PDU mode, of course)
+ * +CMT: [<alpha>],
+ *
+ * Only public for debugging and for RIL
+ *
+ * {@hide}
+ */
+ public static SmsMessage newFromCMT(String[] lines) {
+ // received SMS in 3GPP format
+ SmsMessageBase wrappedMessage =
+ com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
+
+ return new SmsMessage(wrappedMessage);
+ }
+
+ /** @hide */
+ public static SmsMessage newFromParcel(Parcel p) {
+ // received SMS in 3GPP2 format
+ SmsMessageBase wrappedMessage =
+ com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
+
+ return new SmsMessage(wrappedMessage);
+ }
+
+ /**
+ * Create an SmsMessage from an SMS EF record.
+ *
+ * @param index Index of SMS record. This should be index in ArrayList
+ * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param data Record data.
+ * @return An SmsMessage representing the record.
+ *
+ * @hide
+ */
+ public static SmsMessage createFromEfRecord(int index, byte[] data) {
+ SmsMessageBase wrappedMessage;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+ index, data);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+ index, data);
+ }
+
+ return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+ }
+
+ /**
+ * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+ * length in bytes (not hex chars) less the SMSC header
+ *
+ * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+ * We should probably deprecate it and remove the obsolete test case.
+ */
+ public static int getTPLayerLengthForPDU(String pdu) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+ } else {
+ return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+ }
+ }
+
+ /*
+ * TODO(cleanup): It would make some sense if the result of
+ * preprocessing a message to determine the proper encoding (i.e.
+ * the resulting data structure from calculateLength) could be
+ * passed as an argument to the actual final encoding function.
+ * This would better ensure that the logic behind size calculation
+ * actually matched the encoding.
+ */
+
+ /**
+ * 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 if true, characters that are not part of the
+ * radio-specific 7-bit encoding are counted as single
+ * space chars. If false, and if the messageBody contains
+ * non-7-bit encodable characters, length is calculated
+ * using a 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's
+ * required, int[1] the number of code units used, and
+ * int[2] is the number of code units remaining until the
+ * next message. int[3] is an indicator of the encoding
+ * code unit size (see the ENCODING_* definitions in SmsConstants)
+ */
+ public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly) :
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ int ret[] = new int[4];
+ ret[0] = ted.msgCount;
+ ret[1] = ted.codeUnitCount;
+ ret[2] = ted.codeUnitsRemaining;
+ ret[3] = ted.codeUnitSize;
+ return ret;
+ }
+
+ /**
+ * Divide a message text into several fragments, none bigger than
+ * the maximum SMS message text size.
+ *
+ * @param text text, must not be null.
+ * @return an ArrayList
of strings that, in order,
+ * comprise the original msg text
+ *
+ * @hide
+ */
+ public static ArrayList fragmentText(String text) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
+
+ // TODO(cleanup): The code here could be rolled into the logic
+ // below cleanly if these MAX_* constants were defined more
+ // flexibly...
+
+ int limit;
+ if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+ int udhLength;
+ if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+ } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+ } else {
+ udhLength = 0;
+ }
+
+ if (ted.msgCount > 1) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+ }
+
+ if (udhLength != 0) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+ }
+
+ limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
+ } else {
+ if (ted.msgCount > 1) {
+ limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+ } else {
+ limit = SmsConstants.MAX_USER_DATA_BYTES;
+ }
+ }
+
+ int pos = 0; // Index in code units.
+ int textLen = text.length();
+ ArrayList result = new ArrayList(ted.msgCount);
+ while (pos < textLen) {
+ int nextPos = 0; // Counts code units.
+ if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+ if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
+ // For a singleton CDMA message, the encoding must be ASCII...
+ nextPos = pos + Math.min(limit, textLen - pos);
+ } else {
+ // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+ nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
+ ted.languageTable, ted.languageShiftTable);
+ }
+ } else { // Assume unicode.
+ nextPos = pos + Math.min(limit / 2, textLen - pos);
+ }
+ if ((nextPos <= pos) || (nextPos > textLen)) {
+ Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+ nextPos + " >= " + textLen + ")");
+ break;
+ }
+ result.add(text.substring(pos, nextPos));
+ pos = nextPos;
+ }
+ return result;
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the radio
+ * specific (GSM / CDMA) alphabet encoding are converted to as a
+ * single space characters. If false, a messageBody containing
+ * non-GSM or non-CDMA alphabet characters are encoded using
+ * 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ */
+ public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+ return calculateLength((CharSequence)messageBody, use7bitOnly);
+ }
+
+ /*
+ * TODO(cleanup): It looks like there is now no useful reason why
+ * apps should generate pdus themselves using these routines,
+ * instead of handing the raw data to SMSDispatcher (and thereby
+ * have the phone process do the encoding). Moreover, CDMA now
+ * has shared state (in the form of the msgId system property)
+ * which can only be modified by the phone process, and hence
+ * makes the output of these routines incorrect. Since they now
+ * serve no purpose, they should probably just return null
+ * directly, and be deprecated. Going further in that direction,
+ * the above parsers of serialized pdu data should probably also
+ * be gotten rid of, hiding all but the necessarily visible
+ * structured data from client apps. A possible concern with
+ * doing this is that apps may be using these routines to generate
+ * pdus that are then sent elsewhere, some network server, for
+ * example, and that always returning null would thereby break
+ * otherwise useful apps.
+ */
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * This method will not attempt to use any GSM national language 7 bit encodings.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a SubmitPdu
containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message, boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, null);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address & port.
+ * This method will not attempt to use any GSM national language 7 bit encodings.
+ *
+ * @param scAddress Service Centre address. null == use default
+ * @param destinationAddress the address of the destination for the message
+ * @param destinationPort the port to deliver the message to at the
+ * destination
+ * @param data the data for the message
+ * @return a SubmitPdu
containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, short destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Returns the address of the SMS service center that relayed this message
+ * or null if there is none.
+ */
+ public String getServiceCenterAddress() {
+ return mWrappedSmsMessage.getServiceCenterAddress();
+ }
+
+ /**
+ * Returns the originating address (sender) of this SMS message in String
+ * form or null if unavailable
+ */
+ public String getOriginatingAddress() {
+ return mWrappedSmsMessage.getOriginatingAddress();
+ }
+
+ /**
+ * Returns the originating address, or email from address if this message
+ * was from an email gateway. Returns null if originating address
+ * unavailable.
+ */
+ public String getDisplayOriginatingAddress() {
+ return mWrappedSmsMessage.getDisplayOriginatingAddress();
+ }
+
+ /**
+ * Returns the message body as a String, if it exists and is text based.
+ * @return message body is there is one, otherwise null
+ */
+ public String getMessageBody() {
+ return mWrappedSmsMessage.getMessageBody();
+ }
+
+ /**
+ * Returns the class of this message.
+ */
+ public MessageClass getMessageClass() {
+ switch(mWrappedSmsMessage.getMessageClass()) {
+ case CLASS_0: return MessageClass.CLASS_0;
+ case CLASS_1: return MessageClass.CLASS_1;
+ case CLASS_2: return MessageClass.CLASS_2;
+ case CLASS_3: return MessageClass.CLASS_3;
+ default: return MessageClass.UNKNOWN;
+
+ }
+ }
+
+ /**
+ * Returns the message body, or email message body if this message was from
+ * an email gateway. Returns null if message body unavailable.
+ */
+ public String getDisplayMessageBody() {
+ return mWrappedSmsMessage.getDisplayMessageBody();
+ }
+
+ /**
+ * Unofficial convention of a subject line enclosed in parens empty string
+ * if not present
+ */
+ public String getPseudoSubject() {
+ return mWrappedSmsMessage.getPseudoSubject();
+ }
+
+ /**
+ * Returns the service centre timestamp in currentTimeMillis() format
+ */
+ public long getTimestampMillis() {
+ return mWrappedSmsMessage.getTimestampMillis();
+ }
+
+ /**
+ * Returns true if message is an email.
+ *
+ * @return true if this message came through an email gateway and email
+ * sender / subject / parsed body are available
+ */
+ public boolean isEmail() {
+ return mWrappedSmsMessage.isEmail();
+ }
+
+ /**
+ * @return if isEmail() is true, body of the email sent through the gateway.
+ * null otherwise
+ */
+ public String getEmailBody() {
+ return mWrappedSmsMessage.getEmailBody();
+ }
+
+ /**
+ * @return if isEmail() is true, email from address of email sent through
+ * the gateway. null otherwise
+ */
+ public String getEmailFrom() {
+ return mWrappedSmsMessage.getEmailFrom();
+ }
+
+ /**
+ * Get protocol identifier.
+ */
+ public int getProtocolIdentifier() {
+ return mWrappedSmsMessage.getProtocolIdentifier();
+ }
+
+ /**
+ * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
+ * SMS
+ */
+ public boolean isReplace() {
+ return mWrappedSmsMessage.isReplace();
+ }
+
+ /**
+ * Returns true for CPHS MWI toggle message.
+ *
+ * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
+ * B.4.2
+ */
+ public boolean isCphsMwiMessage() {
+ return mWrappedSmsMessage.isCphsMwiMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) clear message
+ */
+ public boolean isMWIClearMessage() {
+ return mWrappedSmsMessage.isMWIClearMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) set message
+ */
+ public boolean isMWISetMessage() {
+ return mWrappedSmsMessage.isMWISetMessage();
+ }
+
+ /**
+ * returns true if this message is a "Message Waiting Indication Group:
+ * Discard Message" notification and should not be stored.
+ */
+ public boolean isMwiDontStore() {
+ return mWrappedSmsMessage.isMwiDontStore();
+ }
+
+ /**
+ * returns the user data section minus the user data header if one was
+ * present.
+ */
+ public byte[] getUserData() {
+ return mWrappedSmsMessage.getUserData();
+ }
+
+ /**
+ * Returns the raw PDU for the message.
+ *
+ * @return the raw PDU for the message.
+ */
+ public byte[] getPdu() {
+ return mWrappedSmsMessage.getPdu();
+ }
+
+ /**
+ * Returns the status of the message on the SIM (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the SIM. These are:
+ * SmsManager.STATUS_ON_SIM_FREE
+ * SmsManager.STATUS_ON_SIM_READ
+ * SmsManager.STATUS_ON_SIM_UNREAD
+ * SmsManager.STATUS_ON_SIM_SEND
+ * SmsManager.STATUS_ON_SIM_UNSENT
+ * @deprecated Use getStatusOnIcc instead.
+ */
+ @Deprecated public int getStatusOnSim() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the status of the message on the ICC (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the ICC. These are:
+ * SmsManager.STATUS_ON_ICC_FREE
+ * SmsManager.STATUS_ON_ICC_READ
+ * SmsManager.STATUS_ON_ICC_UNREAD
+ * SmsManager.STATUS_ON_ICC_SEND
+ * SmsManager.STATUS_ON_ICC_UNSENT
+ */
+ public int getStatusOnIcc() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the SIM (1-based index).
+ * @return the record index of the message on the SIM, or -1 if this
+ * SmsMessage was not created from a SIM SMS EF record.
+ * @deprecated Use getIndexOnIcc instead.
+ */
+ @Deprecated public int getIndexOnSim() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the ICC (1-based index).
+ * @return the record index of the message on the ICC, or -1 if this
+ * SmsMessage was not created from a ICC SMS EF record.
+ */
+ public int getIndexOnIcc() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * GSM:
+ * For an SMS-STATUS-REPORT message, this returns the status field from
+ * the status report. This field indicates the status of a previously
+ * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a
+ * description of values.
+ * CDMA:
+ * For not interfering with status codes from GSM, the value is
+ * shifted to the bits 31-16.
+ * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+ * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+ *
+ * @return 0 indicates the previously sent message was received.
+ * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+ * for a description of other possible values.
+ */
+ public int getStatus() {
+ return mWrappedSmsMessage.getStatus();
+ }
+
+ /**
+ * Return true iff the message is a SMS-STATUS-REPORT message.
+ */
+ public boolean isStatusReportMessage() {
+ return mWrappedSmsMessage.isStatusReportMessage();
+ }
+
+ /**
+ * Returns true iff the TP-Reply-Path
bit is set in
+ * this message.
+ */
+ public boolean isReplyPathPresent() {
+ return mWrappedSmsMessage.isReplyPathPresent();
+ }
+}
diff --git a/src/java/android/telephony/gsm/SmsManager.java b/src/java/android/telephony/gsm/SmsManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b75298b593df0cbed97e42b17429abb8d5379ba
--- /dev/null
+++ b/src/java/android/telephony/gsm/SmsManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gsm;
+
+import android.app.PendingIntent;
+
+import java.util.ArrayList;
+
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ * @deprecated Replaced by android.telephony.SmsManager that supports both GSM and CDMA.
+ */
+@Deprecated public final class SmsManager {
+ private static SmsManager sInstance;
+ private android.telephony.SmsManager mSmsMgrProxy;
+
+ /** Get the default instance of the SmsManager
+ *
+ * @return the default instance of the SmsManager
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public static final SmsManager getDefault() {
+ if (sInstance == null) {
+ sInstance = new SmsManager();
+ }
+ return sInstance;
+ }
+
+ @Deprecated
+ private SmsManager() {
+ mSmsMgrProxy = android.telephony.SmsManager.getDefault();
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mSmsMgrProxy.sendTextMessage(destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Divide a text message into several messages, none bigger than
+ * the maximum SMS message size.
+ *
+ * @param text the original message. Must not be null.
+ * @return an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final ArrayList divideMessage(String text) {
+ return mSmsMgrProxy.divideMessage(text);
+ }
+
+ /**
+ * Send a multi-part text based SMS. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * divideMessage
.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList parts,
+ ArrayList sentIntents, ArrayList deliveryIntents) {
+ mSmsMgrProxy.sendMultipartTextMessage(destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param destinationPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendDataMessage(
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mSmsMgrProxy.sendDataMessage(destinationAddress, scAddress, destinationPort,
+ data, sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Copy a raw SMS PDU to the SIM.
+ *
+ * @param smsc the SMSC for this message, or NULL for the default SMSC
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_SIM_READ, STATUS_ON_SIM_UNREAD,
+ * STATUS_ON_SIM_SENT, STATUS_ON_SIM_UNSENT)
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean copyMessageToSim(byte[] smsc, byte[] pdu, int status) {
+ return mSmsMgrProxy.copyMessageToIcc(smsc, pdu, status);
+ }
+
+ /**
+ * Delete the specified message from the SIM.
+ *
+ * @param messageIndex is the record index of the message on SIM
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean deleteMessageFromSim(int messageIndex) {
+ return mSmsMgrProxy.deleteMessageFromIcc(messageIndex);
+ }
+
+ /**
+ * Update the specified message on the SIM.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_SIM_READ,
+ * STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT,
+ * STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE)
+ * @param pdu the raw PDU to store
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean updateMessageOnSim(int messageIndex, int newStatus, byte[] pdu) {
+ return mSmsMgrProxy.updateMessageOnIcc(messageIndex, newStatus, pdu);
+ }
+
+ /**
+ * Retrieves all messages currently stored on SIM.
+ * @return ArrayList
of SmsMessage
objects
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final ArrayList getAllMessagesFromSim() {
+ return mSmsMgrProxy.getAllMessagesFromIcc();
+ }
+
+ /** Free space (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_FREE = 0;
+
+ /** Received and read (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_READ = 1;
+
+ /** Received and unread (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_UNREAD = 3;
+
+ /** Stored and sent (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_SENT = 5;
+
+ /** Stored and unsent (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_UNSENT = 7;
+
+ /** Generic failure cause
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
+
+ /** Failed because radio was explicitly turned off
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_RADIO_OFF = 2;
+
+ /** Failed because no pdu provided
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_NULL_PDU = 3;
+
+ /** Failed because service is currently unavailable
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_NO_SERVICE = 4;
+
+}
diff --git a/src/java/android/telephony/gsm/SmsMessage.java b/src/java/android/telephony/gsm/SmsMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a814c3e7b0720d1d8d10b585b3695e0248428d6
--- /dev/null
+++ b/src/java/android/telephony/gsm/SmsMessage.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gsm;
+
+import android.os.Parcel;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ * @deprecated Replaced by android.telephony.SmsMessage that supports both GSM and CDMA.
+ */
+@Deprecated
+public class SmsMessage {
+ private static final boolean LOCAL_DEBUG = true;
+ private static final String LOG_TAG = "SMS";
+
+ /**
+ * SMS Class enumeration.
+ * See TS 23.038.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public enum MessageClass{
+ UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+ }
+
+ /** Unknown encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_UNKNOWN = 0;
+
+ /** 7-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_7BIT = 1;
+
+ /** 8-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_8BIT = 2;
+
+ /** 16-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_16BIT = 3;
+
+ /** The maximum number of payload bytes per message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_BYTES = 140;
+
+ /**
+ * The maximum number of payload bytes per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ *
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide pending API Council approval to extend the public API
+ */
+ @Deprecated public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+ /** The maximum number of payload septets per message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_SEPTETS = 160;
+
+ /**
+ * The maximum number of payload septets per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+ /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+ * @deprecated Use android.telephony.SmsMessage.
+ * {@hide}
+ */
+ @Deprecated public SmsMessageBase mWrappedSmsMessage;
+
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public static class SubmitPdu {
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated public byte[] encodedScAddress; // Null if not applicable.
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated public byte[] encodedMessage;
+
+ //Constructor
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public SubmitPdu() {
+ }
+
+ /** @deprecated Use android.telephony.SmsMessage.
+ * {@hide}
+ */
+ @Deprecated
+ protected SubmitPdu(SubmitPduBase spb) {
+ this.encodedMessage = spb.encodedMessage;
+ this.encodedScAddress = spb.encodedScAddress;
+ }
+
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public String toString() {
+ return "SubmitPdu: encodedScAddress = "
+ + Arrays.toString(encodedScAddress)
+ + ", encodedMessage = "
+ + Arrays.toString(encodedMessage);
+ }
+ }
+
+ // Constructor
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public SmsMessage() {
+ this(getSmsFacility());
+ }
+
+ private SmsMessage(SmsMessageBase smb) {
+ mWrappedSmsMessage = smb;
+ }
+
+ /**
+ * Create an SmsMessage from a raw PDU.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SmsMessage createFromPdu(byte[] pdu) {
+ SmsMessageBase wrappedMessage;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+ }
+
+ return new SmsMessage(wrappedMessage);
+ }
+
+ /**
+ * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+ * length in bytes (not hex chars) less the SMSC header
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int getTPLayerLengthForPDU(String pdu) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+ } else {
+ return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+ }
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the GSM
+ * alphabet are counted as a single space char. If false, a
+ * messageBody containing non-GSM alphabet characters is calculated
+ * for 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+ GsmAlphabet.TextEncodingDetails ted =
+ com.android.internal.telephony.gsm.SmsMessage
+ .calculateLength(messageBody, use7bitOnly);
+ int ret[] = new int[4];
+ ret[0] = ted.msgCount;
+ ret[1] = ted.codeUnitCount;
+ ret[2] = ted.codeUnitsRemaining;
+ ret[3] = ted.codeUnitSize;
+ return ret;
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the GSM
+ * alphabet are counted as a single space char. If false, a
+ * messageBody containing non-GSM alphabet characters is calculated
+ * for 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+ return calculateLength((CharSequence)messageBody, use7bitOnly);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a SubmitPdu
containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested,
+ SmsHeader.fromByteArray(header));
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, header);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a SubmitPdu
containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message, boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, null);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ *
+ * @param scAddress Service Centre address. null == use default
+ * @param destinationAddress the address of the destination for the message
+ * @param destinationPort the port to deliver the message to at the
+ * destination
+ * @param data the dat for the message
+ * @return a SubmitPdu
containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, short destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Returns the address of the SMS service center that relayed this message
+ * or null if there is none.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getServiceCenterAddress() {
+ return mWrappedSmsMessage.getServiceCenterAddress();
+ }
+
+ /**
+ * Returns the originating address (sender) of this SMS message in String
+ * form or null if unavailable
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getOriginatingAddress() {
+ return mWrappedSmsMessage.getOriginatingAddress();
+ }
+
+ /**
+ * Returns the originating address, or email from address if this message
+ * was from an email gateway. Returns null if originating address
+ * unavailable.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getDisplayOriginatingAddress() {
+ return mWrappedSmsMessage.getDisplayOriginatingAddress();
+ }
+
+ /**
+ * Returns the message body as a String, if it exists and is text based.
+ * @return message body is there is one, otherwise null
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getMessageBody() {
+ return mWrappedSmsMessage.getMessageBody();
+ }
+
+ /**
+ * Returns the class of this message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public MessageClass getMessageClass() {
+ int index = mWrappedSmsMessage.getMessageClass().ordinal();
+
+ return MessageClass.values()[index];
+ }
+
+ /**
+ * Returns the message body, or email message body if this message was from
+ * an email gateway. Returns null if message body unavailable.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getDisplayMessageBody() {
+ return mWrappedSmsMessage.getDisplayMessageBody();
+ }
+
+ /**
+ * Unofficial convention of a subject line enclosed in parens empty string
+ * if not present
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getPseudoSubject() {
+ return mWrappedSmsMessage.getPseudoSubject();
+ }
+
+ /**
+ * Returns the service centre timestamp in currentTimeMillis() format
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public long getTimestampMillis() {
+ return mWrappedSmsMessage.getTimestampMillis();
+ }
+
+ /**
+ * Returns true if message is an email.
+ *
+ * @return true if this message came through an email gateway and email
+ * sender / subject / parsed body are available
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isEmail() {
+ return mWrappedSmsMessage.isEmail();
+ }
+
+ /**
+ * @return if isEmail() is true, body of the email sent through the gateway.
+ * null otherwise
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getEmailBody() {
+ return mWrappedSmsMessage.getEmailBody();
+ }
+
+ /**
+ * @return if isEmail() is true, email from address of email sent through
+ * the gateway. null otherwise
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getEmailFrom() {
+ return mWrappedSmsMessage.getEmailFrom();
+ }
+
+ /**
+ * Get protocol identifier.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public int getProtocolIdentifier() {
+ return mWrappedSmsMessage.getProtocolIdentifier();
+ }
+
+ /**
+ * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" SMS
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isReplace() {
+ return mWrappedSmsMessage.isReplace();
+ }
+
+ /**
+ * Returns true for CPHS MWI toggle message.
+ *
+ * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section B.4.2
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isCphsMwiMessage() {
+ return mWrappedSmsMessage.isCphsMwiMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) clear message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMWIClearMessage() {
+ return mWrappedSmsMessage.isMWIClearMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) set message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMWISetMessage() {
+ return mWrappedSmsMessage.isMWISetMessage();
+ }
+
+ /**
+ * returns true if this message is a "Message Waiting Indication Group:
+ * Discard Message" notification and should not be stored.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMwiDontStore() {
+ return mWrappedSmsMessage.isMwiDontStore();
+ }
+
+ /**
+ * returns the user data section minus the user data header if one was present.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public byte[] getUserData() {
+ return mWrappedSmsMessage.getUserData();
+ }
+
+ /* Not part of the SDK interface and only needed by specific classes:
+ protected SmsHeader getUserDataHeader()
+ */
+
+ /**
+ * Returns the raw PDU for the message.
+ *
+ * @return the raw PDU for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public byte[] getPdu() {
+ return mWrappedSmsMessage.getPdu();
+ }
+
+ /**
+ * Returns the status of the message on the SIM (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the SIM. These are:
+ * SmsManager.STATUS_ON_SIM_FREE
+ * SmsManager.STATUS_ON_SIM_READ
+ * SmsManager.STATUS_ON_SIM_UNREAD
+ * SmsManager.STATUS_ON_SIM_SEND
+ * SmsManager.STATUS_ON_SIM_UNSENT
+ * @deprecated Use android.telephony.SmsMessage and getStatusOnIcc instead.
+ */
+ @Deprecated
+ public int getStatusOnSim() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the status of the message on the ICC (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the ICC. These are:
+ * SmsManager.STATUS_ON_ICC_FREE
+ * SmsManager.STATUS_ON_ICC_READ
+ * SmsManager.STATUS_ON_ICC_UNREAD
+ * SmsManager.STATUS_ON_ICC_SEND
+ * SmsManager.STATUS_ON_ICC_UNSENT
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public int getStatusOnIcc() {
+
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the SIM (1-based index).
+ * @return the record index of the message on the SIM, or -1 if this
+ * SmsMessage was not created from a SIM SMS EF record.
+ * @deprecated Use android.telephony.SmsMessage and getIndexOnIcc instead.
+ */
+ @Deprecated
+ public int getIndexOnSim() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the ICC (1-based index).
+ * @return the record index of the message on the ICC, or -1 if this
+ * SmsMessage was not created from a ICC SMS EF record.
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public int getIndexOnIcc() {
+
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * GSM:
+ * For an SMS-STATUS-REPORT message, this returns the status field from
+ * the status report. This field indicates the status of a previously
+ * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a
+ * description of values.
+ * CDMA:
+ * For not interfering with status codes from GSM, the value is
+ * shifted to the bits 31-16.
+ * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+ * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+ *
+ * @return 0 indicates the previously sent message was received.
+ * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+ * for a description of other possible values.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public int getStatus() {
+ return mWrappedSmsMessage.getStatus();
+ }
+
+ /**
+ * Return true iff the message is a SMS-STATUS-REPORT message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isStatusReportMessage() {
+ return mWrappedSmsMessage.isStatusReportMessage();
+ }
+
+ /**
+ * Returns true iff the TP-Reply-Path
bit is set in
+ * this message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isReplyPathPresent() {
+ return mWrappedSmsMessage.isReplyPathPresent();
+ }
+
+ /** This method returns the reference to a specific
+ * SmsMessage object, which is used for accessing its static methods.
+ * @return Specific SmsMessage.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ private static final SmsMessageBase getSmsFacility(){
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ if (PHONE_TYPE_CDMA == activePhone) {
+ return new com.android.internal.telephony.cdma.SmsMessage();
+ } else {
+ return new com.android.internal.telephony.gsm.SmsMessage();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ATParseEx.java b/src/java/com/android/internal/telephony/ATParseEx.java
new file mode 100644
index 0000000000000000000000000000000000000000..c93b875e8bd01b7411a01d534684dfef4307df0b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ATParseEx.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class ATParseEx extends RuntimeException
+{
+ public
+ ATParseEx()
+ {
+ super();
+ }
+
+ public
+ ATParseEx(String s)
+ {
+ super(s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ATResponseParser.java b/src/java/com/android/internal/telephony/ATResponseParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdb05262da4c064ffaf81875cd3f72a15ea915e5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ATResponseParser.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class ATResponseParser
+{
+ /*************************** Instance Variables **************************/
+
+ private String line;
+ private int next = 0;
+ private int tokStart, tokEnd;
+
+ /***************************** Class Methods *****************************/
+
+ public
+ ATResponseParser (String line)
+ {
+ this.line = line;
+ }
+
+ public boolean
+ nextBoolean()
+ {
+ // "\s*(\d)(,|$)"
+ // \d is '0' or '1'
+
+ nextTok();
+
+ if (tokEnd - tokStart > 1) {
+ throw new ATParseEx();
+ }
+ char c = line.charAt(tokStart);
+
+ if (c == '0') return false;
+ if (c == '1') return true;
+ throw new ATParseEx();
+ }
+
+
+ /** positive int only */
+ public int
+ nextInt()
+ {
+ // "\s*(\d+)(,|$)"
+ int ret = 0;
+
+ nextTok();
+
+ for (int i = tokStart ; i < tokEnd ; i++) {
+ char c = line.charAt(i);
+
+ // Yes, ASCII decimal digits only
+ if (c < '0' || c > '9') {
+ throw new ATParseEx();
+ }
+
+ ret *= 10;
+ ret += c - '0';
+ }
+
+ return ret;
+ }
+
+ public String
+ nextString()
+ {
+ nextTok();
+
+ return line.substring(tokStart, tokEnd);
+ }
+
+ public boolean
+ hasMore()
+ {
+ return next < line.length();
+ }
+
+ private void
+ nextTok()
+ {
+ int len = line.length();
+
+ if (next == 0) {
+ skipPrefix();
+ }
+
+ if (next >= len) {
+ throw new ATParseEx();
+ }
+
+ try {
+ // \s*("([^"]*)"|(.*)\s*)(,|$)
+
+ char c = line.charAt(next++);
+ boolean hasQuote = false;
+
+ c = skipWhiteSpace(c);
+
+ if (c == '"') {
+ if (next >= len) {
+ throw new ATParseEx();
+ }
+ c = line.charAt(next++);
+ tokStart = next - 1;
+ while (c != '"' && next < len) {
+ c = line.charAt(next++);
+ }
+ if (c != '"') {
+ throw new ATParseEx();
+ }
+ tokEnd = next - 1;
+ if (next < len && line.charAt(next++) != ',') {
+ throw new ATParseEx();
+ }
+ } else {
+ tokStart = next - 1;
+ tokEnd = tokStart;
+ while (c != ',') {
+ if (!Character.isWhitespace(c)) {
+ tokEnd = next;
+ }
+ if (next == len) {
+ break;
+ }
+ c = line.charAt(next++);
+ }
+ }
+ } catch (StringIndexOutOfBoundsException ex) {
+ throw new ATParseEx();
+ }
+ }
+
+
+ /** Throws ATParseEx if whitespace extends to the end of string */
+ private char
+ skipWhiteSpace (char c)
+ {
+ int len;
+ len = line.length();
+ while (next < len && Character.isWhitespace(c)) {
+ c = line.charAt(next++);
+ }
+
+ if (Character.isWhitespace(c)) {
+ throw new ATParseEx();
+ }
+ return c;
+ }
+
+
+ private void
+ skipPrefix()
+ {
+ // consume "^[^:]:"
+
+ next = 0;
+ int s = line.length();
+ while (next < s){
+ char c = line.charAt(next++);
+
+ if (c == ':') {
+ return;
+ }
+ }
+
+ throw new ATParseEx("missing prefix");
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecord.java b/src/java/com/android/internal/telephony/AdnRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bf2d3c61d502c17607c3e50657c1fffd72e590a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AdnRecord.java
@@ -0,0 +1,321 @@
+/*
+ * 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;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+
+/**
+ *
+ * Used to load or store ADNs (Abbreviated Dialing Numbers).
+ *
+ * {@hide}
+ *
+ */
+public class AdnRecord implements Parcelable {
+ static final String LOG_TAG = "GSM";
+
+ //***** Instance Variables
+
+ String alphaTag = null;
+ String number = null;
+ String[] emails;
+ int extRecord = 0xff;
+ int efid; // or 0 if none
+ int recordNumber; // or 0 if none
+
+
+ //***** Constants
+
+ // In an ADN record, everything but the alpha identifier
+ // is in a footer that's 14 bytes
+ static final int FOOTER_SIZE_BYTES = 14;
+
+ // Maximum size of the un-extended number field
+ static final int MAX_NUMBER_SIZE_BYTES = 11;
+
+ static final int EXT_RECORD_LENGTH_BYTES = 13;
+ static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
+ static final int EXT_RECORD_TYPE_MASK = 3;
+ static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
+
+ // ADN offset
+ static final int ADN_BCD_NUMBER_LENGTH = 0;
+ static final int ADN_TON_AND_NPI = 1;
+ static final int ADN_DIALING_NUMBER_START = 2;
+ static final int ADN_DIALING_NUMBER_END = 11;
+ static final int ADN_CAPABILITY_ID = 12;
+ static final int ADN_EXTENSION_ID = 13;
+
+ //***** Static Methods
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public AdnRecord createFromParcel(Parcel source) {
+ int efid;
+ int recordNumber;
+ String alphaTag;
+ String number;
+ String[] emails;
+
+ efid = source.readInt();
+ recordNumber = source.readInt();
+ alphaTag = source.readString();
+ number = source.readString();
+ emails = source.readStringArray();
+
+ return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
+ }
+
+ public AdnRecord[] newArray(int size) {
+ return new AdnRecord[size];
+ }
+ };
+
+
+ //***** Constructor
+ public AdnRecord (byte[] record) {
+ this(0, 0, record);
+ }
+
+ public AdnRecord (int efid, int recordNumber, byte[] record) {
+ this.efid = efid;
+ this.recordNumber = recordNumber;
+ parseRecord(record);
+ }
+
+ public AdnRecord (String alphaTag, String number) {
+ this(0, 0, alphaTag, number);
+ }
+
+ public AdnRecord (String alphaTag, String number, String[] emails) {
+ this(0, 0, alphaTag, number, emails);
+ }
+
+ public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
+ this.efid = efid;
+ this.recordNumber = recordNumber;
+ this.alphaTag = alphaTag;
+ this.number = number;
+ this.emails = emails;
+ }
+
+ public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
+ this.efid = efid;
+ this.recordNumber = recordNumber;
+ this.alphaTag = alphaTag;
+ this.number = number;
+ this.emails = null;
+ }
+
+ //***** Instance Methods
+
+ public String getAlphaTag() {
+ return alphaTag;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public String[] getEmails() {
+ return emails;
+ }
+
+ public void setEmails(String[] emails) {
+ this.emails = emails;
+ }
+
+ public String toString() {
+ return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'";
+ }
+
+ public boolean isEmpty() {
+ return TextUtils.isEmpty(alphaTag) && TextUtils.isEmpty(number) && emails == null;
+ }
+
+ public boolean hasExtendedRecord() {
+ return extRecord != 0 && extRecord != 0xff;
+ }
+
+ /** Helper function for {@link #isEqual}. */
+ private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
+ if (s1 == s2) {
+ return true;
+ }
+ if (s1 == null) {
+ s1 = "";
+ }
+ if (s2 == null) {
+ s2 = "";
+ }
+ return (s1.equals(s2));
+ }
+
+ public boolean isEqual(AdnRecord adn) {
+ return ( stringCompareNullEqualsEmpty(alphaTag, adn.alphaTag) &&
+ stringCompareNullEqualsEmpty(number, adn.number) &&
+ Arrays.equals(emails, adn.emails));
+ }
+ //***** Parcelable Implementation
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(efid);
+ dest.writeInt(recordNumber);
+ dest.writeString(alphaTag);
+ dest.writeString(number);
+ dest.writeStringArray(emails);
+ }
+
+ /**
+ * Build adn hex byte array based on record size
+ * The format of byte array is defined in 51.011 10.5.1
+ *
+ * @param recordSize is the size X of EF record
+ * @return hex byte[recordSize] to be written to EF record
+ * return null for wrong format of dialing number or tag
+ */
+ public byte[] buildAdnString(int recordSize) {
+ byte[] bcdNumber;
+ byte[] byteTag;
+ byte[] adnString;
+ int footerOffset = recordSize - FOOTER_SIZE_BYTES;
+
+ // create an empty record
+ adnString = new byte[recordSize];
+ for (int i = 0; i < recordSize; i++) {
+ adnString[i] = (byte) 0xFF;
+ }
+
+ if (TextUtils.isEmpty(number)) {
+ Log.w(LOG_TAG, "[buildAdnString] Empty dialing number");
+ return adnString; // return the empty record (for delete)
+ } else if (number.length()
+ > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
+ Log.w(LOG_TAG,
+ "[buildAdnString] Max length of dialing number is 20");
+ return null;
+ } else if (alphaTag != null && alphaTag.length() > footerOffset) {
+ Log.w(LOG_TAG,
+ "[buildAdnString] Max length of tag is " + footerOffset);
+ return null;
+ } else {
+ bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(number);
+
+ System.arraycopy(bcdNumber, 0, adnString,
+ footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
+
+ adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
+ = (byte) (bcdNumber.length);
+ adnString[footerOffset + ADN_CAPABILITY_ID]
+ = (byte) 0xFF; // Capability Id
+ adnString[footerOffset + ADN_EXTENSION_ID]
+ = (byte) 0xFF; // Extension Record Id
+
+ if (!TextUtils.isEmpty(alphaTag)) {
+ byteTag = GsmAlphabet.stringToGsm8BitPacked(alphaTag);
+ System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
+ }
+
+ return adnString;
+ }
+ }
+
+ /**
+ * See TS 51.011 10.5.10
+ */
+ public void
+ appendExtRecord (byte[] extRecord) {
+ try {
+ if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
+ return;
+ }
+
+ if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
+ != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
+ return;
+ }
+
+ if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
+ // invalid or empty record
+ return;
+ }
+
+ number += PhoneNumberUtils.calledPartyBCDFragmentToString(
+ extRecord, 2, 0xff & extRecord[1]);
+
+ // We don't support ext record chaining.
+
+ } catch (RuntimeException ex) {
+ Log.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
+ }
+ }
+
+ //***** Private Methods
+
+ /**
+ * alphaTag and number are set to null on invalid format
+ */
+ private void
+ parseRecord(byte[] record) {
+ try {
+ alphaTag = IccUtils.adnStringFieldToString(
+ record, 0, record.length - FOOTER_SIZE_BYTES);
+
+ int footerOffset = record.length - FOOTER_SIZE_BYTES;
+
+ int numberLength = 0xff & record[footerOffset];
+
+ if (numberLength > MAX_NUMBER_SIZE_BYTES) {
+ // Invalid number length
+ number = "";
+ return;
+ }
+
+ // Please note 51.011 10.5.1:
+ //
+ // "If the Dialling Number/SSC String does not contain
+ // a dialling number, e.g. a control string deactivating
+ // a service, the TON/NPI byte shall be set to 'FF' by
+ // the ME (see note 2)."
+
+ number = PhoneNumberUtils.calledPartyBCDToString(
+ record, footerOffset + 1, numberLength);
+
+
+ extRecord = 0xff & record[record.length - 1];
+
+ emails = null;
+
+ } catch (RuntimeException ex) {
+ Log.w(LOG_TAG, "Error parsing AdnRecord", ex);
+ number = "";
+ alphaTag = "";
+ emails = null;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecordCache.java b/src/java/com/android/internal/telephony/AdnRecordCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..db5f4da6bd97da36572bcf3028ab508bff2360bd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AdnRecordCache.java
@@ -0,0 +1,364 @@
+/*
+ * 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.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.telephony.gsm.UsimPhoneBookManager;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * {@hide}
+ */
+public final class AdnRecordCache extends Handler implements IccConstants {
+ //***** Instance Variables
+
+ private IccFileHandler mFh;
+ private UsimPhoneBookManager mUsimPhoneBookManager;
+
+ // Indexed by EF ID
+ SparseArray> adnLikeFiles
+ = new SparseArray>();
+
+ // People waiting for ADN-like files to be loaded
+ SparseArray> adnLikeWaiters
+ = new SparseArray>();
+
+ // People waiting for adn record to be updated
+ SparseArray userWriteResponse = new SparseArray();
+
+ //***** Event Constants
+
+ static final int EVENT_LOAD_ALL_ADN_LIKE_DONE = 1;
+ static final int EVENT_UPDATE_ADN_DONE = 2;
+
+ //***** Constructor
+
+
+
+ public AdnRecordCache(IccFileHandler fh) {
+ mFh = fh;
+ mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, this);
+ }
+
+ //***** Called from SIMRecords
+
+ /**
+ * Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh.
+ */
+ public void reset() {
+ adnLikeFiles.clear();
+ mUsimPhoneBookManager.reset();
+
+ clearWaiters();
+ clearUserWriters();
+
+ }
+
+ private void clearWaiters() {
+ int size = adnLikeWaiters.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList waiters = adnLikeWaiters.valueAt(i);
+ AsyncResult ar = new AsyncResult(null, null, new RuntimeException("AdnCache reset"));
+ notifyWaiters(waiters, ar);
+ }
+ adnLikeWaiters.clear();
+ }
+
+ private void clearUserWriters() {
+ int size = userWriteResponse.size();
+ for (int i = 0; i < size; i++) {
+ sendErrorResponse(userWriteResponse.valueAt(i), "AdnCace reset");
+ }
+ userWriteResponse.clear();
+ }
+
+ /**
+ * @return List of AdnRecords for efid if we've already loaded them this
+ * radio session, or null if we haven't
+ */
+ public ArrayList
+ getRecordsIfLoaded(int efid) {
+ return adnLikeFiles.get(efid);
+ }
+
+ /**
+ * Returns extension ef associated with ADN-like EF or -1 if
+ * we don't know.
+ *
+ * See 3GPP TS 51.011 for this mapping
+ */
+ int extensionEfForEf(int efid) {
+ switch (efid) {
+ case EF_MBDN: return EF_EXT6;
+ case EF_ADN: return EF_EXT1;
+ case EF_SDN: return EF_EXT3;
+ case EF_FDN: return EF_EXT2;
+ case EF_MSISDN: return EF_EXT1;
+ case EF_PBR: return 0; // The EF PBR doesn't have an extension record
+ default: return -1;
+ }
+ }
+
+ private void sendErrorResponse(Message response, String errString) {
+ if (response != null) {
+ Exception e = new RuntimeException(errString);
+ AsyncResult.forMessage(response).exception = e;
+ response.sendToTarget();
+ }
+ }
+
+ /**
+ * Update an ADN-like record in EF by record index
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param adn is the new adn to be stored
+ * @param recordIndex is the 1-based adn record index
+ * @param pin2 is required to update EF_FDN, otherwise must be null
+ * @param response message to be posted when done
+ * response.exception hold the exception in error
+ */
+ public void updateAdnByIndex(int efid, AdnRecord adn, int recordIndex, String pin2,
+ Message response) {
+
+ int extensionEF = extensionEfForEf(efid);
+ if (extensionEF < 0) {
+ sendErrorResponse(response, "EF is not known ADN-like EF:" + efid);
+ return;
+ }
+
+ Message pendingResponse = userWriteResponse.get(efid);
+ if (pendingResponse != null) {
+ sendErrorResponse(response, "Have pending update for EF:" + efid);
+ return;
+ }
+
+ userWriteResponse.put(efid, response);
+
+ new AdnRecordLoader(mFh).updateEF(adn, efid, extensionEF,
+ recordIndex, pin2,
+ obtainMessage(EVENT_UPDATE_ADN_DONE, efid, recordIndex, adn));
+ }
+
+ /**
+ * Replace oldAdn with newAdn in ADN-like record in EF
+ *
+ * The ADN-like records must be read through requestLoadAllAdnLike() before
+ *
+ * @param efid must be one of EF_ADN, EF_FDN, and EF_SDN
+ * @param oldAdn is the adn to be replaced
+ * If oldAdn.isEmpty() is ture, it insert the newAdn
+ * @param newAdn is the adn to be stored
+ * If newAdn.isEmpty() is true, it delete the oldAdn
+ * @param pin2 is required to update EF_FDN, otherwise must be null
+ * @param response message to be posted when done
+ * response.exception hold the exception in error
+ */
+ public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
+ String pin2, Message response) {
+
+ int extensionEF;
+ extensionEF = extensionEfForEf(efid);
+
+ if (extensionEF < 0) {
+ sendErrorResponse(response, "EF is not known ADN-like EF:" + efid);
+ return;
+ }
+
+ ArrayList oldAdnList;
+
+ if (efid == EF_PBR) {
+ oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim();
+ } else {
+ oldAdnList = getRecordsIfLoaded(efid);
+ }
+
+ if (oldAdnList == null) {
+ sendErrorResponse(response, "Adn list not exist for EF:" + efid);
+ return;
+ }
+
+ int index = -1;
+ int count = 1;
+ for (Iterator it = oldAdnList.iterator(); it.hasNext(); ) {
+ if (oldAdn.isEqual(it.next())) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+
+ if (index == -1) {
+ sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
+ return;
+ }
+
+ if (efid == EF_PBR) {
+ AdnRecord foundAdn = oldAdnList.get(index-1);
+ efid = foundAdn.efid;
+ extensionEF = foundAdn.extRecord;
+ index = foundAdn.recordNumber;
+
+ newAdn.efid = efid;
+ newAdn.extRecord = extensionEF;
+ newAdn.recordNumber = index;
+ }
+
+ Message pendingResponse = userWriteResponse.get(efid);
+
+ if (pendingResponse != null) {
+ sendErrorResponse(response, "Have pending update for EF:" + efid);
+ return;
+ }
+
+ userWriteResponse.put(efid, response);
+
+ new AdnRecordLoader(mFh).updateEF(newAdn, efid, extensionEF,
+ index, pin2,
+ obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn));
+ }
+
+
+ /**
+ * Responds with exception (in response) if efid is not a known ADN-like
+ * record
+ */
+ public void
+ requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
+ ArrayList waiters;
+ ArrayList result;
+
+ if (efid == EF_PBR) {
+ result = mUsimPhoneBookManager.loadEfFilesFromUsim();
+ } else {
+ result = getRecordsIfLoaded(efid);
+ }
+
+ // Have we already loaded this efid?
+ if (result != null) {
+ if (response != null) {
+ AsyncResult.forMessage(response).result = result;
+ response.sendToTarget();
+ }
+
+ return;
+ }
+
+ // Have we already *started* loading this efid?
+
+ waiters = adnLikeWaiters.get(efid);
+
+ if (waiters != null) {
+ // There's a pending request for this EF already
+ // just add ourselves to it
+
+ waiters.add(response);
+ return;
+ }
+
+ // Start loading efid
+
+ waiters = new ArrayList();
+ waiters.add(response);
+
+ adnLikeWaiters.put(efid, waiters);
+
+
+ if (extensionEf < 0) {
+ // respond with error if not known ADN-like record
+
+ if (response != null) {
+ AsyncResult.forMessage(response).exception
+ = new RuntimeException("EF is not known ADN-like EF:" + efid);
+ response.sendToTarget();
+ }
+
+ return;
+ }
+
+ new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
+ obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
+ }
+
+ //***** Private methods
+
+ private void
+ notifyWaiters(ArrayList waiters, AsyncResult ar) {
+
+ if (waiters == null) {
+ return;
+ }
+
+ for (int i = 0, s = waiters.size() ; i < s ; i++) {
+ Message waiter = waiters.get(i);
+
+ AsyncResult.forMessage(waiter, ar.result, ar.exception);
+ waiter.sendToTarget();
+ }
+ }
+
+ //***** Overridden from Handler
+
+ public void
+ handleMessage(Message msg) {
+ AsyncResult ar;
+ int efid;
+
+ switch(msg.what) {
+ case EVENT_LOAD_ALL_ADN_LIKE_DONE:
+ /* arg1 is efid, obj.result is ArrayList*/
+ ar = (AsyncResult) msg.obj;
+ efid = msg.arg1;
+ ArrayList waiters;
+
+ waiters = adnLikeWaiters.get(efid);
+ adnLikeWaiters.delete(efid);
+
+ if (ar.exception == null) {
+ adnLikeFiles.put(efid, (ArrayList) ar.result);
+ }
+ notifyWaiters(waiters, ar);
+ break;
+ case EVENT_UPDATE_ADN_DONE:
+ ar = (AsyncResult)msg.obj;
+ efid = msg.arg1;
+ int index = msg.arg2;
+ AdnRecord adn = (AdnRecord) (ar.userObj);
+
+ if (ar.exception == null) {
+ adnLikeFiles.get(efid).set(index - 1, adn);
+ mUsimPhoneBookManager.invalidateCache();
+ }
+
+ Message response = userWriteResponse.get(efid);
+ userWriteResponse.delete(efid);
+
+ AsyncResult.forMessage(response, null, ar.exception);
+ response.sendToTarget();
+ break;
+ }
+
+ }
+
+
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecordLoader.java b/src/java/com/android/internal/telephony/AdnRecordLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..084fae6067f801dccc5ad466714fa296a307846f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AdnRecordLoader.java
@@ -0,0 +1,284 @@
+/*
+ * 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 java.util.ArrayList;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+
+public class AdnRecordLoader extends Handler {
+ final static String LOG_TAG = "RIL_AdnRecordLoader";
+
+ //***** Instance Variables
+
+ private IccFileHandler mFh;
+ int ef;
+ int extensionEF;
+ int pendingExtLoads;
+ Message userResponse;
+ String pin2;
+
+ // For "load one"
+ int recordNumber;
+
+ // for "load all"
+ ArrayList adns; // only valid after EVENT_ADN_LOAD_ALL_DONE
+
+ // Either an AdnRecord or a reference to adns depending
+ // if this is a load one or load all operation
+ Object result;
+
+ //***** Event Constants
+
+ static final int EVENT_ADN_LOAD_DONE = 1;
+ static final int EVENT_EXT_RECORD_LOAD_DONE = 2;
+ static final int EVENT_ADN_LOAD_ALL_DONE = 3;
+ static final int EVENT_EF_LINEAR_RECORD_SIZE_DONE = 4;
+ static final int EVENT_UPDATE_RECORD_DONE = 5;
+
+ //***** Constructor
+
+ public AdnRecordLoader(IccFileHandler fh) {
+ // The telephony unit-test cases may create AdnRecords
+ // in secondary threads
+ super(Looper.getMainLooper());
+ mFh = fh;
+ }
+
+ /**
+ * Resulting AdnRecord is placed in response.obj.result
+ * or response.obj.exception is set
+ */
+ public void
+ loadFromEF(int ef, int extensionEF, int recordNumber,
+ Message response) {
+ this.ef = ef;
+ this.extensionEF = extensionEF;
+ this.recordNumber = recordNumber;
+ this.userResponse = response;
+
+ mFh.loadEFLinearFixed(
+ ef, recordNumber,
+ obtainMessage(EVENT_ADN_LOAD_DONE));
+
+ }
+
+
+ /**
+ * Resulting ArrayList<adnRecord> is placed in response.obj.result
+ * or response.obj.exception is set
+ */
+ public void
+ loadAllFromEF(int ef, int extensionEF,
+ Message response) {
+ this.ef = ef;
+ this.extensionEF = extensionEF;
+ this.userResponse = response;
+
+ mFh.loadEFLinearFixedAll(
+ ef,
+ obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
+
+ }
+
+ /**
+ * Write adn to a EF SIM record
+ * It will get the record size of EF record and compose hex adn array
+ * then write the hex array to EF record
+ *
+ * @param adn is set with alphaTag and phone number
+ * @param ef EF fileid
+ * @param extensionEF extension EF fileid
+ * @param recordNumber 1-based record index
+ * @param pin2 for CHV2 operations, must be null if pin2 is not needed
+ * @param response will be sent to its handler when completed
+ */
+ public void
+ updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber,
+ String pin2, Message response) {
+ this.ef = ef;
+ this.extensionEF = extensionEF;
+ this.recordNumber = recordNumber;
+ this.userResponse = response;
+ this.pin2 = pin2;
+
+ mFh.getEFLinearRecordSize( ef,
+ obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
+ }
+
+ //***** Overridden from Handler
+
+ public void
+ handleMessage(Message msg) {
+ AsyncResult ar;
+ byte data[];
+ AdnRecord adn;
+
+ try {
+ switch (msg.what) {
+ case EVENT_EF_LINEAR_RECORD_SIZE_DONE:
+ ar = (AsyncResult)(msg.obj);
+ adn = (AdnRecord)(ar.userObj);
+
+ if (ar.exception != null) {
+ throw new RuntimeException("get EF record size failed",
+ ar.exception);
+ }
+
+ int[] recordSize = (int[])ar.result;
+ // recordSize is int[3] array
+ // int[0] is the record length
+ // int[1] is the total length of the EF file
+ // int[2] is the number of records in the EF file
+ // So int[0] * int[2] = int[1]
+ if (recordSize.length != 3 || recordNumber > recordSize[2]) {
+ throw new RuntimeException("get wrong EF record size format",
+ ar.exception);
+ }
+
+ data = adn.buildAdnString(recordSize[0]);
+
+ if(data == null) {
+ throw new RuntimeException("wrong ADN format",
+ ar.exception);
+ }
+
+ mFh.updateEFLinearFixed(ef, recordNumber,
+ data, pin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
+
+ pendingExtLoads = 1;
+
+ break;
+ case EVENT_UPDATE_RECORD_DONE:
+ ar = (AsyncResult)(msg.obj);
+ if (ar.exception != null) {
+ throw new RuntimeException("update EF adn record failed",
+ ar.exception);
+ }
+ pendingExtLoads = 0;
+ result = null;
+ break;
+ case EVENT_ADN_LOAD_DONE:
+ ar = (AsyncResult)(msg.obj);
+ data = (byte[])(ar.result);
+
+ if (ar.exception != null) {
+ throw new RuntimeException("load failed", ar.exception);
+ }
+
+ if (false) {
+ Log.d(LOG_TAG,"ADN EF: 0x"
+ + Integer.toHexString(ef)
+ + ":" + recordNumber
+ + "\n" + IccUtils.bytesToHexString(data));
+ }
+
+ adn = new AdnRecord(ef, recordNumber, data);
+ result = adn;
+
+ if (adn.hasExtendedRecord()) {
+ // If we have a valid value in the ext record field,
+ // we're not done yet: we need to read the corresponding
+ // ext record and append it
+
+ pendingExtLoads = 1;
+
+ mFh.loadEFLinearFixed(
+ extensionEF, adn.extRecord,
+ obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
+ }
+ break;
+
+ case EVENT_EXT_RECORD_LOAD_DONE:
+ ar = (AsyncResult)(msg.obj);
+ data = (byte[])(ar.result);
+ adn = (AdnRecord)(ar.userObj);
+
+ if (ar.exception != null) {
+ throw new RuntimeException("load failed", ar.exception);
+ }
+
+ Log.d(LOG_TAG,"ADN extension EF: 0x"
+ + Integer.toHexString(extensionEF)
+ + ":" + adn.extRecord
+ + "\n" + IccUtils.bytesToHexString(data));
+
+ adn.appendExtRecord(data);
+
+ pendingExtLoads--;
+ // result should have been set in
+ // EVENT_ADN_LOAD_DONE or EVENT_ADN_LOAD_ALL_DONE
+ break;
+
+ case EVENT_ADN_LOAD_ALL_DONE:
+ ar = (AsyncResult)(msg.obj);
+ ArrayList datas = (ArrayList)(ar.result);
+
+ if (ar.exception != null) {
+ throw new RuntimeException("load failed", ar.exception);
+ }
+
+ adns = new ArrayList(datas.size());
+ result = adns;
+ pendingExtLoads = 0;
+
+ for(int i = 0, s = datas.size() ; i < s ; i++) {
+ adn = new AdnRecord(ef, 1 + i, datas.get(i));
+ adns.add(adn);
+
+ if (adn.hasExtendedRecord()) {
+ // If we have a valid value in the ext record field,
+ // we're not done yet: we need to read the corresponding
+ // ext record and append it
+
+ pendingExtLoads++;
+
+ mFh.loadEFLinearFixed(
+ extensionEF, adn.extRecord,
+ obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
+ }
+ }
+ break;
+ }
+ } catch (RuntimeException exc) {
+ if (userResponse != null) {
+ AsyncResult.forMessage(userResponse)
+ .exception = exc;
+ userResponse.sendToTarget();
+ // Loading is all or nothing--either every load succeeds
+ // or we fail the whole thing.
+ userResponse = null;
+ }
+ return;
+ }
+
+ if (userResponse != null && pendingExtLoads == 0) {
+ AsyncResult.forMessage(userResponse).result
+ = result;
+
+ userResponse.sendToTarget();
+ userResponse = null;
+ }
+ }
+
+
+}
diff --git a/src/java/com/android/internal/telephony/ApnContext.java b/src/java/com/android/internal/telephony/ApnContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..4817a7bf187b2ed333bdc87d7086d670cd74dd96
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ApnContext.java
@@ -0,0 +1,246 @@
+/*
+ * 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.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Maintain the Apn context
+ */
+public class ApnContext {
+
+ public final String LOG_TAG;
+
+ protected static final boolean DBG = true;
+
+ private final String mApnType;
+
+ private DctConstants.State mState;
+
+ private ArrayList mWaitingApns = null;
+
+ /** A zero indicates that all waiting APNs had a permanent error */
+ private AtomicInteger mWaitingApnsPermanentFailureCountDown;
+
+ private ApnSetting mApnSetting;
+
+ DataConnection mDataConnection;
+
+ DataConnectionAc mDataConnectionAc;
+
+ String mReason;
+
+ int mRetryCount;
+
+ /**
+ * user/app requested connection on this APN
+ */
+ AtomicBoolean mDataEnabled;
+
+ /**
+ * carrier requirements met
+ */
+ AtomicBoolean mDependencyMet;
+
+ public ApnContext(String apnType, String logTag) {
+ mApnType = apnType;
+ mState = DctConstants.State.IDLE;
+ setReason(Phone.REASON_DATA_ENABLED);
+ setRetryCount(0);
+ mDataEnabled = new AtomicBoolean(false);
+ mDependencyMet = new AtomicBoolean(true);
+ mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
+ LOG_TAG = logTag;
+ }
+
+ public String getApnType() {
+ return mApnType;
+ }
+
+ public synchronized DataConnection getDataConnection() {
+ return mDataConnection;
+ }
+
+ public synchronized void setDataConnection(DataConnection dc) {
+ if (DBG) {
+ log("setDataConnection: old dc=" + mDataConnection + " new dc=" + dc + " this=" + this);
+ }
+ mDataConnection = dc;
+ }
+
+
+ public synchronized DataConnectionAc getDataConnectionAc() {
+ return mDataConnectionAc;
+ }
+
+ public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
+ if (DBG) {
+ log("setDataConnectionAc: old dcac=" + mDataConnectionAc + " new dcac=" + dcac);
+ }
+ if (dcac != null) {
+ dcac.addApnContextSync(this);
+ } else {
+ if (mDataConnectionAc != null) {
+ mDataConnectionAc.removeApnContextSync(this);
+ }
+ }
+ mDataConnectionAc = dcac;
+ }
+
+ public synchronized ApnSetting getApnSetting() {
+ return mApnSetting;
+ }
+
+ public synchronized void setApnSetting(ApnSetting apnSetting) {
+ mApnSetting = apnSetting;
+ }
+
+ public synchronized void setWaitingApns(ArrayList waitingApns) {
+ mWaitingApns = waitingApns;
+ mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
+ }
+
+ public int getWaitingApnsPermFailCount() {
+ return mWaitingApnsPermanentFailureCountDown.get();
+ }
+
+ public void decWaitingApnsPermFailCount() {
+ mWaitingApnsPermanentFailureCountDown.decrementAndGet();
+ }
+
+ public synchronized ApnSetting getNextWaitingApn() {
+ ArrayList list = mWaitingApns;
+ ApnSetting apn = null;
+
+ if (list != null) {
+ if (!list.isEmpty()) {
+ apn = list.get(0);
+ }
+ }
+ return apn;
+ }
+
+ public synchronized void removeWaitingApn(ApnSetting apn) {
+ if (mWaitingApns != null) {
+ mWaitingApns.remove(apn);
+ }
+ }
+
+ public synchronized ArrayList getWaitingApns() {
+ return mWaitingApns;
+ }
+
+ public synchronized void setState(DctConstants.State s) {
+ if (DBG) {
+ log("setState: " + s + ", previous state:" + mState);
+ }
+
+ mState = s;
+
+ if (mState == DctConstants.State.FAILED) {
+ if (mWaitingApns != null) {
+ mWaitingApns.clear(); // when teardown the connection and set to IDLE
+ }
+ }
+ }
+
+ public synchronized DctConstants.State getState() {
+ return mState;
+ }
+
+ public boolean isDisconnected() {
+ DctConstants.State currentState = getState();
+ return ((currentState == DctConstants.State.IDLE) ||
+ currentState == DctConstants.State.FAILED);
+ }
+
+ public synchronized void setReason(String reason) {
+ if (DBG) {
+ log("set reason as " + reason + ",current state " + mState);
+ }
+ mReason = reason;
+ }
+
+ public synchronized String getReason() {
+ return mReason;
+ }
+
+ public synchronized void setRetryCount(int retryCount) {
+ if (DBG) {
+ log("setRetryCount: " + retryCount);
+ }
+ mRetryCount = retryCount;
+ DataConnection dc = mDataConnection;
+ if (dc != null) {
+ dc.setRetryCount(retryCount);
+ }
+ }
+
+ public synchronized int getRetryCount() {
+ return mRetryCount;
+ }
+
+ public boolean isReady() {
+ return mDataEnabled.get() && mDependencyMet.get();
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (DBG) {
+ log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
+ }
+ mDataEnabled.set(enabled);
+ }
+
+ public boolean isEnabled() {
+ return mDataEnabled.get();
+ }
+
+ public void setDependencyMet(boolean met) {
+ if (DBG) {
+ log("set mDependencyMet as " + met + " current state is " + mDependencyMet.get());
+ }
+ mDependencyMet.set(met);
+ }
+
+ public boolean getDependencyMet() {
+ return mDependencyMet.get();
+ }
+
+ @Override
+ public String toString() {
+ // We don't print mDataConnection because its recursive.
+ return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns=" + mWaitingApns +
+ " mWaitingApnsPermanentFailureCountDown=" + mWaitingApnsPermanentFailureCountDown +
+ " mApnSetting=" + mApnSetting + " mDataConnectionAc=" + mDataConnectionAc +
+ " mReason=" + mReason + " mRetryCount=" + mRetryCount +
+ " mDataEnabled=" + mDataEnabled + " mDependencyMet=" + mDependencyMet + "}";
+ }
+
+ protected void log(String s) {
+ Log.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("ApnContext: " + this.toString());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ApnSetting.java b/src/java/com/android/internal/telephony/ApnSetting.java
new file mode 100755
index 0000000000000000000000000000000000000000..b84c69c1bfe3768d329f884c73b0dbc6224f0b9e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ApnSetting.java
@@ -0,0 +1,200 @@
+/*
+ * 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;
+
+/**
+ * This class represents a apn setting for create PDP link
+ */
+public class ApnSetting {
+
+ static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
+
+ public final String carrier;
+ public final String apn;
+ public final String proxy;
+ public final String port;
+ public final String mmsc;
+ public final String mmsProxy;
+ public final String mmsPort;
+ public final String user;
+ public final String password;
+ public final int authType;
+ public final String[] types;
+ public final int id;
+ public final String numeric;
+ public final String protocol;
+ public final String roamingProtocol;
+ /**
+ * Current status of APN
+ * true : enabled APN, false : disabled APN.
+ */
+ public final boolean carrierEnabled;
+ /**
+ * Radio Access Technology info
+ * To check what values can hold, refer to ServiceState.java.
+ * This should be spread to other technologies,
+ * but currently only used for LTE(14) and EHRPD(13).
+ */
+ public final int bearer;
+
+ public ApnSetting(int id, String numeric, String carrier, String apn,
+ String proxy, String port,
+ String mmsc, String mmsProxy, String mmsPort,
+ String user, String password, int authType, String[] types,
+ String protocol, String roamingProtocol, boolean carrierEnabled, int bearer) {
+ this.id = id;
+ this.numeric = numeric;
+ this.carrier = carrier;
+ this.apn = apn;
+ this.proxy = proxy;
+ this.port = port;
+ this.mmsc = mmsc;
+ this.mmsProxy = mmsProxy;
+ this.mmsPort = mmsPort;
+ this.user = user;
+ this.password = password;
+ this.authType = authType;
+ this.types = types;
+ this.protocol = protocol;
+ this.roamingProtocol = roamingProtocol;
+ this.carrierEnabled = carrierEnabled;
+ this.bearer = bearer;
+ }
+
+ /**
+ * Creates an ApnSetting object from a string.
+ *
+ * @param data the string to read.
+ *
+ * The string must be in one of two formats (newlines added for clarity,
+ * spaces are optional):
+ *
+ * v1 format:
+ * , , , , , ,
+ * , , , , ,,
+ * [, ...]
+ *
+ * v2 format:
+ * [ApnSettingV2] , , , , , ,
+ * , , , , , ,
+ * [| ...], , , ,
+ *
+ * Note that the strings generated by toString() do not contain the username
+ * and password and thus cannot be read by this method.
+ *
+ * @see ApnSettingTest
+ */
+ public static ApnSetting fromString(String data) {
+ if (data == null) return null;
+
+ int version;
+ // matches() operates on the whole string, so append .* to the regex.
+ if (data.matches(V2_FORMAT_REGEX + ".*")) {
+ version = 2;
+ data = data.replaceFirst(V2_FORMAT_REGEX, "");
+ } else {
+ version = 1;
+ }
+
+ String[] a = data.split("\\s*,\\s*");
+ if (a.length < 14) {
+ return null;
+ }
+
+ int authType;
+ try {
+ authType = Integer.parseInt(a[12]);
+ } catch (Exception e) {
+ authType = 0;
+ }
+
+ String[] typeArray;
+ String protocol, roamingProtocol;
+ boolean carrierEnabled;
+ int bearer;
+ if (version == 1) {
+ typeArray = new String[a.length - 13];
+ System.arraycopy(a, 13, typeArray, 0, a.length - 13);
+ protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
+ roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
+ carrierEnabled = true;
+ bearer = 0;
+ } else {
+ if (a.length < 18) {
+ return null;
+ }
+ typeArray = a[13].split("\\s*\\|\\s*");
+ protocol = a[14];
+ roamingProtocol = a[15];
+ try {
+ carrierEnabled = Boolean.parseBoolean(a[16]);
+ } catch (Exception e) {
+ carrierEnabled = true;
+ }
+ bearer = Integer.parseInt(a[17]);
+ }
+
+ 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);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ApnSettingV2] ")
+ .append(carrier)
+ .append(", ").append(id)
+ .append(", ").append(numeric)
+ .append(", ").append(apn)
+ .append(", ").append(proxy)
+ .append(", ").append(mmsc)
+ .append(", ").append(mmsProxy)
+ .append(", ").append(mmsPort)
+ .append(", ").append(port)
+ .append(", ").append(authType).append(", ");
+ for (int i = 0; i < types.length; i++) {
+ sb.append(types[i]);
+ if (i < types.length - 1) {
+ sb.append(" | ");
+ }
+ }
+ sb.append(", ").append(protocol);
+ sb.append(", ").append(roamingProtocol);
+ sb.append(", ").append(carrierEnabled);
+ sb.append(", ").append(bearer);
+ return sb.toString();
+ }
+
+ public boolean canHandleType(String type) {
+ for (String t : types) {
+ // DEFAULT handles all, and HIPRI is handled by DEFAULT
+ if (t.equalsIgnoreCase(type) ||
+ t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL) ||
+ (t.equalsIgnoreCase(PhoneConstants.APN_TYPE_DEFAULT) &&
+ type.equalsIgnoreCase(PhoneConstants.APN_TYPE_HIPRI))) {
+ 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
+ public boolean equals(Object o) {
+ if (o instanceof ApnSetting == false) return false;
+ return (this.toString().equals(o.toString()));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b54656bbf4c0a28d79bab134f2432097fc03a78
--- /dev/null
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -0,0 +1,647 @@
+/*
+ * 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.content.Context;
+import android.os.RegistrantList;
+import android.os.Registrant;
+import android.os.Handler;
+import android.os.AsyncResult;
+import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * {@hide}
+ */
+public abstract class BaseCommands implements CommandsInterface {
+ static final String LOG_TAG = "RILB";
+
+ //***** Instance Variables
+ protected Context mContext;
+ protected RadioState mState = RadioState.RADIO_UNAVAILABLE;
+ protected Object mStateMonitor = new Object();
+
+ protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList();
+ protected RegistrantList mOnRegistrants = new RegistrantList();
+ protected RegistrantList mAvailRegistrants = new RegistrantList();
+ protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList();
+ protected RegistrantList mNotAvailRegistrants = new RegistrantList();
+ protected RegistrantList mCallStateRegistrants = new RegistrantList();
+ protected RegistrantList mVoiceNetworkStateRegistrants = new RegistrantList();
+ protected RegistrantList mDataNetworkStateRegistrants = new RegistrantList();
+ protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
+ protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList();
+ protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
+ protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList();
+ protected Registrant mUnsolOemHookRawRegistrant;
+ protected RegistrantList mOtaProvisionRegistrants = new RegistrantList();
+ protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList();
+ protected RegistrantList mDisplayInfoRegistrants = new RegistrantList();
+ protected RegistrantList mSignalInfoRegistrants = new RegistrantList();
+ protected RegistrantList mNumberInfoRegistrants = new RegistrantList();
+ protected RegistrantList mRedirNumInfoRegistrants = new RegistrantList();
+ protected RegistrantList mLineControlInfoRegistrants = new RegistrantList();
+ protected RegistrantList mT53ClirInfoRegistrants = new RegistrantList();
+ protected RegistrantList mT53AudCntrlInfoRegistrants = new RegistrantList();
+ protected RegistrantList mRingbackToneRegistrants = new RegistrantList();
+ protected RegistrantList mResendIncallMuteRegistrants = new RegistrantList();
+ protected RegistrantList mCdmaSubscriptionChangedRegistrants = new RegistrantList();
+ protected RegistrantList mCdmaPrlChangedRegistrants = new RegistrantList();
+ protected RegistrantList mExitEmergencyCallbackModeRegistrants = new RegistrantList();
+ protected RegistrantList mRilConnectedRegistrants = new RegistrantList();
+ protected RegistrantList mIccRefreshRegistrants = new RegistrantList();
+
+ protected Registrant mGsmSmsRegistrant;
+ protected Registrant mCdmaSmsRegistrant;
+ protected Registrant mNITZTimeRegistrant;
+ protected Registrant mSignalStrengthRegistrant;
+ protected Registrant mUSSDRegistrant;
+ protected Registrant mSmsOnSimRegistrant;
+ protected Registrant mSmsStatusRegistrant;
+ protected Registrant mSsnRegistrant;
+ protected Registrant mCatSessionEndRegistrant;
+ protected Registrant mCatProCmdRegistrant;
+ protected Registrant mCatEventRegistrant;
+ protected Registrant mCatCallSetUpRegistrant;
+ protected Registrant mIccSmsFullRegistrant;
+ protected Registrant mEmergencyCallbackModeRegistrant;
+ protected Registrant mRingRegistrant;
+ protected Registrant mRestrictedStateRegistrant;
+ protected Registrant mGsmBroadcastSmsRegistrant;
+
+ // Preferred network type received from PhoneFactory.
+ // This is used when establishing a connection to the
+ // vendor ril so it starts up in the correct mode.
+ protected int mPreferredNetworkType;
+ // CDMA subscription received from PhoneFactory
+ protected int mCdmaSubscription;
+ // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone.
+ protected int mPhoneType;
+ // RIL Version
+ protected int mRilVersion = -1;
+
+ public BaseCommands(Context context) {
+ mContext = context; // May be null (if so we won't log statistics)
+ }
+
+ //***** CommandsInterface implementation
+
+ public RadioState getRadioState() {
+ return mState;
+ }
+
+ public void registerForRadioStateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mRadioStateChangedRegistrants.add(r);
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForRadioStateChanged(Handler h) {
+ synchronized (mStateMonitor) {
+ mRadioStateChangedRegistrants.remove(h);
+ }
+ }
+
+ public void registerForOn(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mOnRegistrants.add(r);
+
+ if (mState.isOn()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+ public void unregisterForOn(Handler h) {
+ synchronized (mStateMonitor) {
+ mOnRegistrants.remove(h);
+ }
+ }
+
+
+ public void registerForAvailable(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mAvailRegistrants.add(r);
+
+ if (mState.isAvailable()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+
+ public void unregisterForAvailable(Handler h) {
+ synchronized(mStateMonitor) {
+ mAvailRegistrants.remove(h);
+ }
+ }
+
+ public void registerForNotAvailable(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mNotAvailRegistrants.add(r);
+
+ if (!mState.isAvailable()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+
+ public void unregisterForNotAvailable(Handler h) {
+ synchronized (mStateMonitor) {
+ mNotAvailRegistrants.remove(h);
+ }
+ }
+
+ public void registerForOffOrNotAvailable(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mOffOrNotAvailRegistrants.add(r);
+
+ if (mState == RadioState.RADIO_OFF || !mState.isAvailable()) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+ public void unregisterForOffOrNotAvailable(Handler h) {
+ synchronized(mStateMonitor) {
+ mOffOrNotAvailRegistrants.remove(h);
+ }
+ }
+
+ public void registerForCallStateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mCallStateRegistrants.add(r);
+ }
+
+ public void unregisterForCallStateChanged(Handler h) {
+ mCallStateRegistrants.remove(h);
+ }
+
+ public void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mVoiceNetworkStateRegistrants.add(r);
+ }
+
+ public void unregisterForVoiceNetworkStateChanged(Handler h) {
+ mVoiceNetworkStateRegistrants.remove(h);
+ }
+
+ public void registerForDataNetworkStateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mDataNetworkStateRegistrants.add(r);
+ }
+
+ public void unregisterForDataNetworkStateChanged(Handler h) {
+ mDataNetworkStateRegistrants.remove(h);
+ }
+
+ public void registerForVoiceRadioTechChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mVoiceRadioTechChangedRegistrants.add(r);
+ }
+
+ public void unregisterForVoiceRadioTechChanged(Handler h) {
+ mVoiceRadioTechChangedRegistrants.remove(h);
+ }
+
+ public void registerForIccStatusChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mIccStatusChangedRegistrants.add(r);
+ }
+
+ public void unregisterForIccStatusChanged(Handler h) {
+ mIccStatusChangedRegistrants.remove(h);
+ }
+
+ public void setOnNewGsmSms(Handler h, int what, Object obj) {
+ mGsmSmsRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnNewGsmSms(Handler h) {
+ mGsmSmsRegistrant.clear();
+ }
+
+ public void setOnNewCdmaSms(Handler h, int what, Object obj) {
+ mCdmaSmsRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnNewCdmaSms(Handler h) {
+ mCdmaSmsRegistrant.clear();
+ }
+
+ public void setOnNewGsmBroadcastSms(Handler h, int what, Object obj) {
+ mGsmBroadcastSmsRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnNewGsmBroadcastSms(Handler h) {
+ mGsmBroadcastSmsRegistrant.clear();
+ }
+
+ public void setOnSmsOnSim(Handler h, int what, Object obj) {
+ mSmsOnSimRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnSmsOnSim(Handler h) {
+ mSmsOnSimRegistrant.clear();
+ }
+
+ public void setOnSmsStatus(Handler h, int what, Object obj) {
+ mSmsStatusRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnSmsStatus(Handler h) {
+ mSmsStatusRegistrant.clear();
+ }
+
+ public void setOnSignalStrengthUpdate(Handler h, int what, Object obj) {
+ mSignalStrengthRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnSignalStrengthUpdate(Handler h) {
+ mSignalStrengthRegistrant.clear();
+ }
+
+ public void setOnNITZTime(Handler h, int what, Object obj) {
+ mNITZTimeRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnNITZTime(Handler h) {
+ mNITZTimeRegistrant.clear();
+ }
+
+ public void setOnUSSD(Handler h, int what, Object obj) {
+ mUSSDRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnUSSD(Handler h) {
+ mUSSDRegistrant.clear();
+ }
+
+ public void setOnSuppServiceNotification(Handler h, int what, Object obj) {
+ mSsnRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnSuppServiceNotification(Handler h) {
+ mSsnRegistrant.clear();
+ }
+
+ public void setOnCatSessionEnd(Handler h, int what, Object obj) {
+ mCatSessionEndRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnCatSessionEnd(Handler h) {
+ mCatSessionEndRegistrant.clear();
+ }
+
+ public void setOnCatProactiveCmd(Handler h, int what, Object obj) {
+ mCatProCmdRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnCatProactiveCmd(Handler h) {
+ mCatProCmdRegistrant.clear();
+ }
+
+ public void setOnCatEvent(Handler h, int what, Object obj) {
+ mCatEventRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnCatEvent(Handler h) {
+ mCatEventRegistrant.clear();
+ }
+
+ public void setOnCatCallSetUp(Handler h, int what, Object obj) {
+ mCatCallSetUpRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnCatCallSetUp(Handler h) {
+ mCatCallSetUpRegistrant.clear();
+ }
+
+ public void setOnIccSmsFull(Handler h, int what, Object obj) {
+ mIccSmsFullRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnIccSmsFull(Handler h) {
+ mIccSmsFullRegistrant.clear();
+ }
+
+ public void registerForIccRefresh(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mIccRefreshRegistrants.add(r);
+ }
+ public void setOnIccRefresh(Handler h, int what, Object obj) {
+ registerForIccRefresh(h, what, obj);
+ }
+
+ public void setEmergencyCallbackMode(Handler h, int what, Object obj) {
+ mEmergencyCallbackModeRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unregisterForIccRefresh(Handler h) {
+ mIccRefreshRegistrants.remove(h);
+ }
+ public void unsetOnIccRefresh(Handler h) {
+ unregisterForIccRefresh(h);
+ }
+
+ public void setOnCallRing(Handler h, int what, Object obj) {
+ mRingRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnCallRing(Handler h) {
+ mRingRegistrant.clear();
+ }
+
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mVoicePrivacyOnRegistrants.add(r);
+ }
+
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mVoicePrivacyOnRegistrants.remove(h);
+ }
+
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mVoicePrivacyOffRegistrants.add(r);
+ }
+
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mVoicePrivacyOffRegistrants.remove(h);
+ }
+
+ public void setOnRestrictedStateChanged(Handler h, int what, Object obj) {
+ mRestrictedStateRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnRestrictedStateChanged(Handler h) {
+ mRestrictedStateRegistrant.clear();
+ }
+
+ public void registerForDisplayInfo(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mDisplayInfoRegistrants.add(r);
+ }
+
+ public void unregisterForDisplayInfo(Handler h) {
+ mDisplayInfoRegistrants.remove(h);
+ }
+
+ public void registerForCallWaitingInfo(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mCallWaitingInfoRegistrants.add(r);
+ }
+
+ public void unregisterForCallWaitingInfo(Handler h) {
+ mCallWaitingInfoRegistrants.remove(h);
+ }
+
+ public void registerForSignalInfo(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mSignalInfoRegistrants.add(r);
+ }
+
+ public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
+ mUnsolOemHookRawRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnUnsolOemHookRaw(Handler h) {
+ mUnsolOemHookRawRegistrant.clear();
+ }
+
+ public void unregisterForSignalInfo(Handler h) {
+ mSignalInfoRegistrants.remove(h);
+ }
+
+ public void registerForCdmaOtaProvision(Handler h,int what, Object obj){
+ Registrant r = new Registrant (h, what, obj);
+ mOtaProvisionRegistrants.add(r);
+ }
+
+ public void unregisterForCdmaOtaProvision(Handler h){
+ mOtaProvisionRegistrants.remove(h);
+ }
+
+ public void registerForNumberInfo(Handler h,int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mNumberInfoRegistrants.add(r);
+ }
+
+ public void unregisterForNumberInfo(Handler h){
+ mNumberInfoRegistrants.remove(h);
+ }
+
+ public void registerForRedirectedNumberInfo(Handler h,int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mRedirNumInfoRegistrants.add(r);
+ }
+
+ public void unregisterForRedirectedNumberInfo(Handler h) {
+ mRedirNumInfoRegistrants.remove(h);
+ }
+
+ public void registerForLineControlInfo(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mLineControlInfoRegistrants.add(r);
+ }
+
+ public void unregisterForLineControlInfo(Handler h) {
+ mLineControlInfoRegistrants.remove(h);
+ }
+
+ public void registerFoT53ClirlInfo(Handler h,int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mT53ClirInfoRegistrants.add(r);
+ }
+
+ public void unregisterForT53ClirInfo(Handler h) {
+ mT53ClirInfoRegistrants.remove(h);
+ }
+
+ public void registerForT53AudioControlInfo(Handler h,int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mT53AudCntrlInfoRegistrants.add(r);
+ }
+
+ public void unregisterForT53AudioControlInfo(Handler h) {
+ mT53AudCntrlInfoRegistrants.remove(h);
+ }
+
+ public void registerForRingbackTone(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mRingbackToneRegistrants.add(r);
+ }
+
+ public void unregisterForRingbackTone(Handler h) {
+ mRingbackToneRegistrants.remove(h);
+ }
+
+ public void registerForResendIncallMute(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mResendIncallMuteRegistrants.add(r);
+ }
+
+ public void unregisterForResendIncallMute(Handler h) {
+ mResendIncallMuteRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mCdmaSubscriptionChangedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForCdmaSubscriptionChanged(Handler h) {
+ mCdmaSubscriptionChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForCdmaPrlChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mCdmaPrlChangedRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForCdmaPrlChanged(Handler h) {
+ mCdmaPrlChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mExitEmergencyCallbackModeRegistrants.add(r);
+ }
+
+ @Override
+ public void unregisterForExitEmergencyCallbackMode(Handler h) {
+ mExitEmergencyCallbackModeRegistrants.remove(h);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void registerForRilConnected(Handler h, int what, Object obj) {
+ Log.d(LOG_TAG, "registerForRilConnected h=" + h + " w=" + what);
+ Registrant r = new Registrant (h, what, obj);
+ mRilConnectedRegistrants.add(r);
+ if (mRilVersion != -1) {
+ Log.d(LOG_TAG, "Notifying: ril connected mRilVersion=" + mRilVersion);
+ r.notifyRegistrant(new AsyncResult(null, new Integer(mRilVersion), null));
+ }
+ }
+
+ @Override
+ public void unregisterForRilConnected(Handler h) {
+ mRilConnectedRegistrants.remove(h);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setCurrentPreferredNetworkType() {
+ }
+
+ //***** Protected Methods
+ /**
+ * Store new RadioState and send notification based on the changes
+ *
+ * This function is called only by RIL.java when receiving unsolicited
+ * RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED
+ *
+ * RadioState has 3 values : RADIO_OFF, RADIO_UNAVAILABLE, RADIO_ON.
+ *
+ * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED
+ */
+ protected void setRadioState(RadioState newState) {
+ RadioState oldState;
+
+ synchronized (mStateMonitor) {
+ if (false) {
+ Log.v(LOG_TAG, "setRadioState old: " + mState
+ + " new " + newState);
+ }
+
+ oldState = mState;
+ mState = newState;
+
+ if (oldState == mState) {
+ // no state transition
+ return;
+ }
+
+ mRadioStateChangedRegistrants.notifyRegistrants();
+
+ if (mState.isAvailable() && !oldState.isAvailable()) {
+ Log.d(LOG_TAG,"Notifying: radio available");
+ mAvailRegistrants.notifyRegistrants();
+ onRadioAvailable();
+ }
+
+ if (!mState.isAvailable() && oldState.isAvailable()) {
+ Log.d(LOG_TAG,"Notifying: radio not available");
+ mNotAvailRegistrants.notifyRegistrants();
+ }
+
+ if (mState.isOn() && !oldState.isOn()) {
+ Log.d(LOG_TAG,"Notifying: Radio On");
+ mOnRegistrants.notifyRegistrants();
+ }
+
+ if ((!mState.isOn() || !mState.isAvailable())
+ && !((!oldState.isOn() || !oldState.isAvailable()))
+ ) {
+ Log.d(LOG_TAG,"Notifying: radio off or not available");
+ mOffOrNotAvailRegistrants.notifyRegistrants();
+ }
+ }
+ }
+
+ protected void onRadioAvailable() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getLteOnCdmaMode() {
+ return TelephonyManager.getLteOnCdmaModeStatic();
+ }
+
+ @Override
+ public void testingEmergencyCall() {}
+}
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
new file mode 100644
index 0000000000000000000000000000000000000000..4967ab81e5fc12ebd4e738d78e288eb6520a2c32
--- /dev/null
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import java.util.List;
+
+import android.util.Log;
+
+/**
+ * {@hide}
+ */
+public abstract class Call {
+ /* Enums */
+
+ public enum State {
+ IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
+
+ public boolean isAlive() {
+ return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
+ }
+
+ public boolean isRinging() {
+ return this == INCOMING || this == WAITING;
+ }
+
+ public boolean isDialing() {
+ return this == DIALING || this == ALERTING;
+ }
+ }
+
+
+ /* Instance Variables */
+
+ public State state = State.IDLE;
+
+
+ // Flag to indicate if the current calling/caller information
+ // is accurate. If false the information is known to be accurate.
+ //
+ // For CDMA, during call waiting/3 way, there is no network response
+ // if call waiting is answered, network timed out, dropped, 3 way
+ // merged, etc.
+ protected boolean isGeneric = false;
+
+ protected final String LOG_TAG = "Call";
+
+ /* Instance Methods */
+
+ /** Do not modify the List result!!! This list is not yours to keep
+ * It will change across event loop iterations top
+ */
+
+ public abstract List getConnections();
+ public abstract Phone getPhone();
+ public abstract boolean isMultiparty();
+ public abstract void hangup() throws CallStateException;
+
+
+ /**
+ * hasConnection
+ *
+ * @param c a Connection object
+ * @return true if the call contains the connection object passed in
+ */
+ public boolean hasConnection(Connection c) {
+ return c.getCall() == this;
+ }
+
+ /**
+ * hasConnections
+ * @return true if the call contains one or more connections
+ */
+ public boolean hasConnections() {
+ List connections = getConnections();
+
+ if (connections == null) {
+ return false;
+ }
+
+ return connections.size() > 0;
+ }
+
+ /**
+ * getState
+ * @return state of class call
+ */
+ public State getState() {
+ return state;
+ }
+
+ /**
+ * isIdle
+ *
+ * FIXME rename
+ * @return true if the call contains only disconnected connections (if any)
+ */
+ public boolean isIdle() {
+ return !getState().isAlive();
+ }
+
+ /**
+ * Returns the Connection associated with this Call that was created
+ * first, or null if there are no Connections in this Call
+ */
+ public Connection
+ getEarliestConnection() {
+ List l;
+ long time = Long.MAX_VALUE;
+ Connection c;
+ Connection earliest = null;
+
+ l = getConnections();
+
+ if (l.size() == 0) {
+ return null;
+ }
+
+ for (int i = 0, s = l.size() ; i < s ; i++) {
+ c = (Connection) l.get(i);
+ long t;
+
+ t = c.getCreateTime();
+
+ if (t < time) {
+ earliest = c;
+ time = t;
+ }
+ }
+
+ return earliest;
+ }
+
+ public long
+ getEarliestCreateTime() {
+ List l;
+ long time = Long.MAX_VALUE;
+
+ l = getConnections();
+
+ if (l.size() == 0) {
+ return 0;
+ }
+
+ for (int i = 0, s = l.size() ; i < s ; i++) {
+ Connection c = (Connection) l.get(i);
+ long t;
+
+ t = c.getCreateTime();
+
+ time = t < time ? t : time;
+ }
+
+ return time;
+ }
+
+ public long
+ getEarliestConnectTime() {
+ long time = Long.MAX_VALUE;
+ List l = getConnections();
+
+ if (l.size() == 0) {
+ return 0;
+ }
+
+ for (int i = 0, s = l.size() ; i < s ; i++) {
+ Connection c = (Connection) l.get(i);
+ long t;
+
+ t = c.getConnectTime();
+
+ time = t < time ? t : time;
+ }
+
+ return time;
+ }
+
+
+ public boolean
+ isDialingOrAlerting() {
+ return getState().isDialing();
+ }
+
+ public boolean
+ isRinging() {
+ return getState().isRinging();
+ }
+
+ /**
+ * Returns the Connection associated with this Call that was created
+ * last, or null if there are no Connections in this Call
+ */
+ public Connection
+ getLatestConnection() {
+ List l = getConnections();
+ if (l.size() == 0) {
+ return null;
+ }
+
+ long time = 0;
+ Connection latest = null;
+ for (int i = 0, s = l.size() ; i < s ; i++) {
+ Connection c = (Connection) l.get(i);
+ long t = c.getCreateTime();
+
+ if (t > time) {
+ latest = c;
+ time = t;
+ }
+ }
+
+ return latest;
+ }
+
+ /**
+ * To indicate if the connection information is accurate
+ * or not. false means accurate. Only used for CDMA.
+ */
+ public boolean isGeneric() {
+ return isGeneric;
+ }
+
+ /**
+ * Set the generic instance variable
+ */
+ public void setGeneric(boolean generic) {
+ isGeneric = generic;
+ }
+
+ /**
+ * Hangup call if it is alive
+ */
+ public void hangupIfAlive() {
+ if (getState().isAlive()) {
+ try {
+ hangup();
+ } catch (CallStateException ex) {
+ Log.w(LOG_TAG, " hangupIfActive: caught " + ex);
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CallForwardInfo.java b/src/java/com/android/internal/telephony/CallForwardInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b853b0d124bb48ab036d073d4e0f84b1a0f878f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallForwardInfo.java
@@ -0,0 +1,42 @@
+/*
+ * 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.telephony.PhoneNumberUtils;
+
+/**
+ * See also RIL_CallForwardInfo in include/telephony/ril.h
+ *
+ * {@hide}
+ */
+public class CallForwardInfo {
+ public int status; /*1 = active, 0 = not active */
+ public int reason; /* from TS 27.007 7.11 "reason" */
+ public int serviceClass; /* Sum of CommandsInterface.SERVICE_CLASS */
+ public int toa; /* "type" from TS 27.007 7.11 */
+ public String number; /* "number" from TS 27.007 7.11 */
+ public int timeSeconds; /* for CF no reply only */
+
+ public String toString() {
+ return super.toString() + (status == 0 ? " not active " : " active ")
+ + " reason: " + reason
+ + " serviceClass: " + serviceClass
+ + " \"" + PhoneNumberUtils.stringFromStringAndTOA(number, toa) + "\" "
+ + timeSeconds + " seconds";
+
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..b87ea50c64fd332c28ade65fb982dd9e0c7d3112
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -0,0 +1,1855 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.sip.SipPhone;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.os.Registrant;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+
+/**
+ * @hide
+ *
+ * CallManager class provides an abstract layer for PhoneApp to access
+ * and control calls. It implements Phone interface.
+ *
+ * CallManager provides call and connection control as well as
+ * channel capability.
+ *
+ * There are three categories of APIs CallManager provided
+ *
+ * 1. Call control and operation, such as dial() and hangup()
+ * 2. Channel capabilities, such as CanConference()
+ * 3. Register notification
+ *
+ *
+ */
+public final class CallManager {
+
+ private static final String LOG_TAG ="CallManager";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private static final int EVENT_DISCONNECT = 100;
+ private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
+ private static final int EVENT_NEW_RINGING_CONNECTION = 102;
+ private static final int EVENT_UNKNOWN_CONNECTION = 103;
+ private static final int EVENT_INCOMING_RING = 104;
+ private static final int EVENT_RINGBACK_TONE = 105;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_ON = 106;
+ private static final int EVENT_IN_CALL_VOICE_PRIVACY_OFF = 107;
+ private static final int EVENT_CALL_WAITING = 108;
+ private static final int EVENT_DISPLAY_INFO = 109;
+ private static final int EVENT_SIGNAL_INFO = 110;
+ private static final int EVENT_CDMA_OTA_STATUS_CHANGE = 111;
+ private static final int EVENT_RESEND_INCALL_MUTE = 112;
+ private static final int EVENT_MMI_INITIATE = 113;
+ private static final int EVENT_MMI_COMPLETE = 114;
+ private static final int EVENT_ECM_TIMER_RESET = 115;
+ private static final int EVENT_SUBSCRIPTION_INFO_READY = 116;
+ private static final int EVENT_SUPP_SERVICE_FAILED = 117;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 118;
+ private static final int EVENT_POST_DIAL_CHARACTER = 119;
+
+ // Singleton instance
+ private static final CallManager INSTANCE = new CallManager();
+
+ // list of registered phones, which are PhoneBase objs
+ private final ArrayList mPhones;
+
+ // list of supported ringing calls
+ private final ArrayList mRingingCalls;
+
+ // list of supported background calls
+ private final ArrayList mBackgroundCalls;
+
+ // list of supported foreground calls
+ private final ArrayList mForegroundCalls;
+
+ // empty connection list
+ private final ArrayList emptyConnections = new ArrayList();
+
+ // default phone as the first phone registered, which is PhoneBase obj
+ private Phone mDefaultPhone;
+
+ // state registrants
+ protected final RegistrantList mPreciseCallStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mNewRingingConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mIncomingRingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisconnectRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mUnknownConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mRingbackToneRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOnRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mInCallVoicePrivacyOffRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCallWaitingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisplayInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSignalInfoRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mCdmaOtaStatusChangeRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mResendIncallMuteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiInitiateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiCompleteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mEcmTimerResetRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSubscriptionInfoReadyRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSuppServiceFailedRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mServiceStateChangedRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mPostDialCharacterRegistrants
+ = new RegistrantList();
+
+ private CallManager() {
+ mPhones = new ArrayList();
+ mRingingCalls = new ArrayList();
+ mBackgroundCalls = new ArrayList();
+ mForegroundCalls = new ArrayList();
+ mDefaultPhone = null;
+ }
+
+ /**
+ * get singleton instance of CallManager
+ * @return CallManager
+ */
+ public static CallManager getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Get the corresponding PhoneBase obj
+ *
+ * @param phone a Phone object
+ * @return the corresponding PhoneBase obj in Phone if Phone
+ * is a PhoneProxy obj
+ * or the Phone itself if Phone is not a PhoneProxy obj
+ */
+ private static Phone getPhoneBase(Phone phone) {
+ if (phone instanceof PhoneProxy) {
+ return phone.getForegroundCall().getPhone();
+ }
+ return phone;
+ }
+
+ /**
+ * Check if two phones refer to the same PhoneBase obj
+ *
+ * Note: PhoneBase, not PhoneProxy, is to be used inside of CallManager
+ *
+ * Both PhoneBase and PhoneProxy implement Phone interface, so
+ * they have same phone APIs, such as dial(). The real implementation, for
+ * example in GSM, is in GSMPhone as extend from PhoneBase, so that
+ * foregroundCall.getPhone() returns GSMPhone obj. On the other hand,
+ * PhoneFactory.getDefaultPhone() returns PhoneProxy obj, which has a class
+ * member of GSMPhone.
+ *
+ * So for phone returned by PhoneFacotry, which is used by PhoneApp,
+ * phone.getForegroundCall().getPhone() != phone
+ * but
+ * isSamePhone(phone, phone.getForegroundCall().getPhone()) == true
+ *
+ * @param p1 is the first Phone obj
+ * @param p2 is the second Phone obj
+ * @return true if p1 and p2 refer to the same phone
+ */
+ public static boolean isSamePhone(Phone p1, Phone p2) {
+ return (getPhoneBase(p1) == getPhoneBase(p2));
+ }
+
+ /**
+ * Returns all the registered phone objects.
+ * @return all the registered phone objects.
+ */
+ public List getAllPhones() {
+ return Collections.unmodifiableList(mPhones);
+ }
+
+ /**
+ * Get current coarse-grained voice call state.
+ * If the Call Manager has an active call and call waiting occurs,
+ * then the phone state is RINGING not OFFHOOK
+ *
+ */
+ public PhoneConstants.State getState() {
+ PhoneConstants.State s = PhoneConstants.State.IDLE;
+
+ for (Phone phone : mPhones) {
+ if (phone.getState() == PhoneConstants.State.RINGING) {
+ s = PhoneConstants.State.RINGING;
+ } else if (phone.getState() == PhoneConstants.State.OFFHOOK) {
+ if (s == PhoneConstants.State.IDLE) s = PhoneConstants.State.OFFHOOK;
+ }
+ }
+ return s;
+ }
+
+ /**
+ * @return the service state of CallManager, which represents the
+ * highest priority state of all the service states of phones
+ *
+ * The priority is defined as
+ *
+ * STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF
+ *
+ */
+
+ public int getServiceState() {
+ int resultState = ServiceState.STATE_OUT_OF_SERVICE;
+
+ for (Phone phone : mPhones) {
+ int serviceState = phone.getServiceState().getState();
+ if (serviceState == ServiceState.STATE_IN_SERVICE) {
+ // IN_SERVICE has the highest priority
+ resultState = serviceState;
+ break;
+ } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ // OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF
+ // Note: EMERGENCY_ONLY is not in use at this moment
+ if ( resultState == ServiceState.STATE_EMERGENCY_ONLY ||
+ resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ } else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
+ if (resultState == ServiceState.STATE_POWER_OFF) {
+ resultState = serviceState;
+ }
+ }
+ }
+ return resultState;
+ }
+
+ /**
+ * Register phone to CallManager
+ * @param phone to be registered
+ * @return true if register successfully
+ */
+ public boolean registerPhone(Phone phone) {
+ Phone basePhone = getPhoneBase(phone);
+
+ if (basePhone != null && !mPhones.contains(basePhone)) {
+
+ if (DBG) {
+ Log.d(LOG_TAG, "registerPhone(" +
+ phone.getPhoneName() + " " + phone + ")");
+ }
+
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = basePhone;
+ }
+ mPhones.add(basePhone);
+ mRingingCalls.add(basePhone.getRingingCall());
+ mBackgroundCalls.add(basePhone.getBackgroundCall());
+ mForegroundCalls.add(basePhone.getForegroundCall());
+ registerForPhoneStates(basePhone);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * unregister phone from CallManager
+ * @param phone to be unregistered
+ */
+ public void unregisterPhone(Phone phone) {
+ Phone basePhone = getPhoneBase(phone);
+
+ if (basePhone != null && mPhones.contains(basePhone)) {
+
+ if (DBG) {
+ Log.d(LOG_TAG, "unregisterPhone(" +
+ phone.getPhoneName() + " " + phone + ")");
+ }
+
+ mPhones.remove(basePhone);
+ mRingingCalls.remove(basePhone.getRingingCall());
+ mBackgroundCalls.remove(basePhone.getBackgroundCall());
+ mForegroundCalls.remove(basePhone.getForegroundCall());
+ unregisterForPhoneStates(basePhone);
+ if (basePhone == mDefaultPhone) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = null;
+ } else {
+ mDefaultPhone = mPhones.get(0);
+ }
+ }
+ }
+ }
+
+ /**
+ * return the default phone or null if no phone available
+ */
+ public Phone getDefaultPhone() {
+ return mDefaultPhone;
+ }
+
+ /**
+ * @return the phone associated with the foreground call
+ */
+ public Phone getFgPhone() {
+ return getActiveFgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the background call
+ */
+ public Phone getBgPhone() {
+ return getFirstActiveBgCall().getPhone();
+ }
+
+ /**
+ * @return the phone associated with the ringing call
+ */
+ public Phone getRingingPhone() {
+ return getFirstActiveRingingCall().getPhone();
+ }
+
+ public void setAudioMode() {
+ Context context = getContext();
+ if (context == null) return;
+ AudioManager audioManager = (AudioManager)
+ context.getSystemService(Context.AUDIO_SERVICE);
+
+ // change the audio mode and request/abandon audio focus according to phone state,
+ // but only on audio mode transitions
+ switch (getState()) {
+ case RINGING:
+ if (audioManager.getMode() != AudioManager.MODE_RINGTONE) {
+ // only request audio focus if the ringtone is going to be heard
+ if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
+ if (VDBG) Log.d(LOG_TAG, "requestAudioFocus on STREAM_RING");
+ audioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ }
+ audioManager.setMode(AudioManager.MODE_RINGTONE);
+ }
+ break;
+ case OFFHOOK:
+ Phone offhookPhone = getFgPhone();
+ if (getActiveFgCallState() == Call.State.IDLE) {
+ // There is no active Fg calls, the OFFHOOK state
+ // is set by the Bg call. So set the phone to bgPhone.
+ offhookPhone = getBgPhone();
+ }
+
+ int newAudioMode = AudioManager.MODE_IN_CALL;
+ if (offhookPhone instanceof SipPhone) {
+ // enable IN_COMMUNICATION audio mode instead for sipPhone
+ newAudioMode = AudioManager.MODE_IN_COMMUNICATION;
+ }
+ if (audioManager.getMode() != newAudioMode) {
+ // request audio focus before setting the new mode
+ if (VDBG) Log.d(LOG_TAG, "requestAudioFocus on STREAM_VOICE_CALL");
+ audioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ audioManager.setMode(newAudioMode);
+ }
+ break;
+ case IDLE:
+ if (audioManager.getMode() != AudioManager.MODE_NORMAL) {
+ audioManager.setMode(AudioManager.MODE_NORMAL);
+ if (VDBG) Log.d(LOG_TAG, "abandonAudioFocus");
+ // abandon audio focus after the mode has been set back to normal
+ audioManager.abandonAudioFocusForCall();
+ }
+ break;
+ }
+ }
+
+ private Context getContext() {
+ Phone defaultPhone = getDefaultPhone();
+ return ((defaultPhone == null) ? null : defaultPhone.getContext());
+ }
+
+ private void registerForPhoneStates(Phone phone) {
+ // for common events supported by all phones
+ phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
+ phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
+ phone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null);
+ phone.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null);
+ phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null);
+ phone.registerForRingbackTone(mHandler, EVENT_RINGBACK_TONE, null);
+ phone.registerForInCallVoicePrivacyOn(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null);
+ phone.registerForInCallVoicePrivacyOff(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null);
+ phone.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null);
+ phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null);
+ phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null);
+ phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
+ phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
+ phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null);
+ phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null);
+
+ // for events supported only by GSM and CDMA phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ phone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL_CHARACTER, null);
+ }
+
+ // for events supported only by CDMA phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ){
+ phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null);
+ phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null);
+ phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null);
+ phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null);
+ }
+ }
+
+ private void unregisterForPhoneStates(Phone phone) {
+ // for common events supported by all phones
+ phone.unregisterForPreciseCallStateChanged(mHandler);
+ phone.unregisterForDisconnect(mHandler);
+ phone.unregisterForNewRingingConnection(mHandler);
+ phone.unregisterForUnknownConnection(mHandler);
+ phone.unregisterForIncomingRing(mHandler);
+ phone.unregisterForRingbackTone(mHandler);
+ phone.unregisterForInCallVoicePrivacyOn(mHandler);
+ phone.unregisterForInCallVoicePrivacyOff(mHandler);
+ phone.unregisterForDisplayInfo(mHandler);
+ phone.unregisterForSignalInfo(mHandler);
+ phone.unregisterForResendIncallMute(mHandler);
+ phone.unregisterForMmiInitiate(mHandler);
+ phone.unregisterForMmiComplete(mHandler);
+ phone.unregisterForSuppServiceFailed(mHandler);
+ phone.unregisterForServiceStateChanged(mHandler);
+
+ // for events supported only by GSM and CDMA phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
+ phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null);
+ }
+
+ // for events supported only by CDMA phone
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ){
+ phone.unregisterForCdmaOtaStatusChange(mHandler);
+ phone.unregisterForSubscriptionInfoReady(mHandler);
+ phone.unregisterForCallWaiting(mHandler);
+ phone.unregisterForEcmTimerReset(mHandler);
+ }
+ }
+
+ /**
+ * Answers a ringing or waiting call.
+ *
+ * Active call, if any, go on hold.
+ * If active call can't be held, i.e., a background call of the same channel exists,
+ * the active call will be hang up.
+ *
+ * Answering occurs asynchronously, and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when call is not ringing or waiting
+ */
+ public void acceptCall(Call ringingCall) throws CallStateException {
+ Phone ringingPhone = ringingCall.getPhone();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "acceptCall(" +ringingCall + " from " + ringingCall.getPhone() + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
+ boolean sameChannel = (activePhone == ringingPhone);
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
+ }
+
+ if (sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ } else if (!sameChannel && !hasBgCall) {
+ activePhone.switchHoldingAndActive();
+ } else if (!sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ }
+ }
+
+ ringingPhone.acceptCall();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Reject (ignore) a ringing call. In GSM, this means UDUB
+ * (User Determined User Busy). Reject occurs asynchronously,
+ * and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when no call is ringing or waiting
+ */
+ public void rejectCall(Call ringingCall) throws CallStateException {
+ if (VDBG) {
+ Log.d(LOG_TAG, "rejectCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ Phone ringingPhone = ringingCall.getPhone();
+
+ ringingPhone.rejectCall();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End rejectCall(" +ringingCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Places active call on hold, and makes held call active.
+ * Switch occurs asynchronously and may fail.
+ *
+ * There are 4 scenarios
+ * 1. only active call but no held call, aka, hold
+ * 2. no active call but only held call, aka, unhold
+ * 3. both active and held calls from same phone, aka, swap
+ * 4. active and held calls from different phones, aka, phone swap
+ *
+ * Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if active call is ringing, waiting, or
+ * dialing/alerting, or heldCall can't be active.
+ * In these cases, this operation may not be performed.
+ */
+ public void switchHoldingAndActive(Call heldCall) throws CallStateException {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "switchHoldingAndActive(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ if (activePhone != null) {
+ activePhone.switchHoldingAndActive();
+ }
+
+ if (heldPhone != null && heldPhone != activePhone) {
+ heldPhone.switchHoldingAndActive();
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End switchHoldingAndActive(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Hangup foreground call and resume the specific background call
+ *
+ * Note: this is noop if there is no foreground call or the heldCall is null
+ *
+ * @param heldCall to become foreground
+ * @throws CallStateException
+ */
+ public void hangupForegroundResumeBackground(Call heldCall) throws CallStateException {
+ Phone foregroundPhone = null;
+ Phone backgroundPhone = null;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "hangupForegroundResumeBackground(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ foregroundPhone = getFgPhone();
+ if (heldCall != null) {
+ backgroundPhone = heldCall.getPhone();
+ if (foregroundPhone == backgroundPhone) {
+ getActiveFgCall().hangup();
+ } else {
+ // the call to be hangup and resumed belongs to different phones
+ getActiveFgCall().hangup();
+ switchHoldingAndActive(heldCall);
+ }
+ }
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End hangupForegroundResumeBackground(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Whether or not the phone can conference in the current phone
+ * state--that is, one call holding and one call active.
+ * @return true if the phone can conference; false otherwise.
+ */
+ public boolean canConference(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return heldPhone.getClass().equals(activePhone.getClass());
+ }
+
+ /**
+ * Conferences holding and active. Conference occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canConference() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void conference(Call heldCall) throws CallStateException {
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "conference(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+
+ Phone fgPhone = getFgPhone();
+ if (fgPhone instanceof SipPhone) {
+ ((SipPhone) fgPhone).conference(heldCall);
+ } else if (canConference(heldCall)) {
+ fgPhone.conference();
+ } else {
+ throw(new CallStateException("Can't conference foreground and selected background call"));
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End conference(" +heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @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.
+ */
+ public Connection dial(Phone phone, String dialString) throws CallStateException {
+ Phone basePhone = getPhoneBase(phone);
+ Connection result;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (!canDial(phone)) {
+ throw new CallStateException("cannot dial in current state");
+ }
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
+
+ if (DBG) {
+ Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == basePhone));
+ }
+
+ if (activePhone != basePhone) {
+ if (hasBgCall) {
+ Log.d(LOG_TAG, "Hangup");
+ getActiveFgCall().hangup();
+ } else {
+ Log.d(LOG_TAG, "Switch");
+ activePhone.switchHoldingAndActive();
+ }
+ }
+ }
+
+ result = basePhone.dial(dialString);
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End dial(" + basePhone + ", "+ dialString + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ return result;
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @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.
+ */
+ public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException {
+ return phone.dial(dialString, uusInfo);
+ }
+
+ /**
+ * clear disconnect connection for each phone
+ */
+ public void clearDisconnected() {
+ for(Phone phone : mPhones) {
+ phone.clearDisconnected();
+ }
+ }
+
+ /**
+ * Phone can make a call only if ALL of the following are true:
+ * - Phone is not powered off
+ * - There's no incoming or waiting call
+ * - There's available call slot in either foreground or background
+ * - The foreground call is ACTIVE or IDLE or DISCONNECTED.
+ * (We mainly need to make sure it *isn't* DIALING or ALERTING.)
+ * @param phone
+ * @return true if the phone can make a new call
+ */
+ private boolean canDial(Phone phone) {
+ int serviceState = phone.getServiceState().getState();
+ boolean hasRingingCall = hasActiveRingingCall();
+ boolean hasActiveCall = hasActiveFgCall();
+ boolean hasHoldingCall = hasActiveBgCall();
+ boolean allLinesTaken = hasActiveCall && hasHoldingCall;
+ Call.State fgCallState = getActiveFgCallState();
+
+ boolean result = (serviceState != ServiceState.STATE_POWER_OFF
+ && !hasRingingCall
+ && !allLinesTaken
+ && ((fgCallState == Call.State.ACTIVE)
+ || (fgCallState == Call.State.IDLE)
+ || (fgCallState == Call.State.DISCONNECTED)));
+
+ if (result == false) {
+ Log.d(LOG_TAG, "canDial serviceState=" + serviceState
+ + " hasRingingCall=" + hasRingingCall
+ + " hasActiveCall=" + hasActiveCall
+ + " hasHoldingCall=" + hasHoldingCall
+ + " allLinesTaken=" + allLinesTaken
+ + " fgCallState=" + fgCallState);
+ }
+ return result;
+ }
+
+ /**
+ * Whether or not the phone can do explicit call transfer in the current
+ * phone state--that is, one call holding and one call active.
+ * @return true if the phone can do explicit call transfer; false otherwise.
+ */
+ public boolean canTransfer(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return (heldPhone == activePhone && activePhone.canTransfer());
+ }
+
+ /**
+ * Connects the held call and active call
+ * Disconnects the subscriber from both calls
+ *
+ * Explicit Call Transfer occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canTransfer() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void explicitCallTransfer(Call heldCall) throws CallStateException {
+ if (VDBG) {
+ Log.d(LOG_TAG, " explicitCallTransfer(" + heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (canTransfer(heldCall)) {
+ heldCall.getPhone().explicitCallTransfer();
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End explicitCallTransfer(" + heldCall + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ }
+
+ /**
+ * Returns a list of MMI codes that are pending for a phone. (They have initiated
+ * but have not yet completed).
+ * Presently there is only ever one.
+ *
+ * Use registerForMmiInitiate
+ * and registerForMmiComplete
for change notification.
+ * @return null if phone doesn't have or support mmi code
+ */
+ public List extends MmiCode> getPendingMmiCodes(Phone phone) {
+ Log.e(LOG_TAG, "getPendingMmiCodes not implemented");
+ return null;
+ }
+
+ /**
+ * Sends user response to a USSD REQUEST message. An MmiCode instance
+ * representing this response is sent to handlers registered with
+ * registerForMmiInitiate.
+ *
+ * @param ussdMessge Message to send in the response.
+ * @return false if phone doesn't support ussd service
+ */
+ public boolean sendUssdResponse(Phone phone, String ussdMessge) {
+ Log.e(LOG_TAG, "sendUssdResponse not implemented");
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the microphone for the active call. The microphone
+ * is automatically unmuted if a call is answered, dialed, or resumed
+ * from a holding state.
+ *
+ * @param muted true to mute the microphone,
+ * false to activate the microphone.
+ */
+
+ public void setMute(boolean muted) {
+ if (VDBG) {
+ Log.d(LOG_TAG, " setMute(" + muted + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setMute(muted);
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End setMute(" + muted + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Gets current mute status. Use
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
+ * as a change notifcation, although presently phone state changed is not
+ * fired when setMute() is called.
+ *
+ * @return true is muting, false is unmuting
+ */
+ public boolean getMute() {
+ if (hasActiveFgCall()) {
+ return getActiveFgCall().getPhone().getMute();
+ } else if (hasActiveBgCall()) {
+ return getFirstActiveBgCall().getPhone().getMute();
+ }
+ return false;
+ }
+
+ /**
+ * Enables or disables echo suppression.
+ */
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ if (VDBG) {
+ Log.d(LOG_TAG, " setEchoSuppression(" + enabled + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setEchoSuppressionEnabled(enabled);
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End setEchoSuppression(" + enabled + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * Play a DTMF tone on the active call.
+ *
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean sendDtmf(char c) {
+ boolean result = false;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " sendDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ result = true;
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End sendDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+ return result;
+ }
+
+ /**
+ * Start to paly a DTMF tone on the active call.
+ * or there is a playing DTMF tone.
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ *
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean startDtmf(char c) {
+ boolean result = false;
+
+ if (VDBG) {
+ Log.d(LOG_TAG, " startDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().startDtmf(c);
+ result = true;
+ }
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End startDtmf(" + c + ")");
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ return result;
+ }
+
+ /**
+ * Stop the playing DTMF tone. Ignored if there is no playing DTMF
+ * tone or no active call.
+ */
+ public void stopDtmf() {
+ if (VDBG) {
+ Log.d(LOG_TAG, " stopDtmf()" );
+ Log.d(LOG_TAG, this.toString());
+ }
+
+ if (hasActiveFgCall()) getFgPhone().stopDtmf();
+
+ if (VDBG) {
+ Log.d(LOG_TAG, "End stopDtmf()");
+ Log.d(LOG_TAG, this.toString());
+ }
+ }
+
+ /**
+ * send burst DTMF tone, it can send the string as single character or multiple character
+ * ignore if there is no active call or not valid digits string.
+ * Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
+ * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character,
+ * this api can send single character and multiple character, also, this api has response
+ * back to caller.
+ *
+ * @param dtmfString is string representing the dialing digit(s) in the active call
+ * @param on the DTMF ON length in milliseconds, or 0 for default
+ * @param off the DTMF OFF length in milliseconds, or 0 for default
+ * @param onComplete is the callback message when the action is processed by BP
+ *
+ */
+ public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies when a voice connection has disconnected, either due to local
+ * or remote hangup or error.
+ *
+ * Messages received from this will have the following members:
+ *
- Message.obj will be an AsyncResult
+ * - AsyncResult.userObj = obj
+ * - AsyncResult.result = a Connection object that is
+ * no longer connected.
+ */
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ mDisconnectRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice disconnection notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForDisconnect(Handler h){
+ mDisconnectRegistrants.remove(h);
+ }
+
+ /**
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
+ * Resulting events will have an AsyncResult in Message.obj
.
+ * AsyncResult.userData will be set to the obj argument here.
+ * The h parameter is held only by a weak reference.
+ */
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice call state change notifications.
+ * Extraneous calls are tolerated silently.
+ */
+ public void unregisterForPreciseCallStateChanged(Handler h){
+ mPreciseCallStateRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when a previously untracked non-ringing/waiting connection has appeared.
+ * This is likely due to some other entity (eg, SIM card application) initiating a call.
+ */
+ public void registerForUnknownConnection(Handler h, int what, Object obj){
+ mUnknownConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for unknown connection notifications.
+ */
+ public void unregisterForUnknownConnection(Handler h){
+ mUnknownConnectionRegistrants.remove(h);
+ }
+
+
+ /**
+ * Notifies when a new ringing or waiting connection has appeared.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection.
+ * Please check Connection.isRinging() to make sure the Connection
+ * has not dropped since this message was posted.
+ * If Connection.isRinging() is true, then
+ * Connection.getCall() == Phone.getRingingCall()
+ */
+ public void registerForNewRingingConnection(Handler h, int what, Object obj){
+ mNewRingingConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new ringing connection notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForNewRingingConnection(Handler h){
+ mNewRingingConnectionRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when an incoming call rings.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection.
+ */
+ public void registerForIncomingRing(Handler h, int what, Object obj){
+ mIncomingRingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ring notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForIncomingRing(Handler h){
+ mIncomingRingRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when out-band ringback tone is needed.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play ringback tone
+ * and false to stop.
+ */
+ public void registerForRingbackTone(Handler h, int what, Object obj){
+ mRingbackToneRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ringback tone notification.
+ */
+
+ public void unregisterForRingbackTone(Handler h){
+ mRingbackToneRegistrants.remove(h);
+ }
+
+ /**
+ * Registers the handler to reset the uplink mute state to get
+ * uplink audio.
+ */
+ public void registerForResendIncallMute(Handler h, int what, Object obj){
+ mResendIncallMuteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for resend incall mute notifications.
+ */
+ public void unregisterForResendIncallMute(Handler h){
+ mResendIncallMuteRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications of initiation of a new MMI code request.
+ * MMI codes for GSM are discussed in 3GPP TS 22.030.
+ *
+ * Example: If Phone.dial is called with "*#31#", then the app will
+ * be notified here.
+ *
+ * The returned Message.obj
will contain an AsyncResult.
+ *
+ * obj.result
will be an "MmiCode" object.
+ */
+ public void registerForMmiInitiate(Handler h, int what, Object obj){
+ mMmiInitiateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for new MMI initiate notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiInitiate(Handler h){
+ mMmiInitiateRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications that an MMI request has completed
+ * its network activity and is in its final state. This may mean a state
+ * of COMPLETE, FAILED, or CANCELLED.
+ *
+ * Message.obj
will contain an AsyncResult.
+ * obj.result
will be an "MmiCode" object
+ */
+ public void registerForMmiComplete(Handler h, int what, Object obj){
+ mMmiCompleteRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for MMI complete notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiComplete(Handler h){
+ mMmiCompleteRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj){
+ mEcmTimerResetRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h){
+ mEcmTimerResetRegistrants.remove(h);
+ }
+
+ /**
+ * Register for ServiceState changed.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a ServiceState instance
+ */
+ public void registerForServiceStateChanged(Handler h, int what, Object obj){
+ mServiceStateChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for ServiceStateChange notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForServiceStateChanged(Handler h){
+ mServiceStateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a supplementary service attempt fails.
+ * Message.obj will contain an AsyncResult.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj){
+ mSuppServiceFailedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a supplementary service attempt fails.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSuppServiceFailed(Handler h){
+ mSuppServiceFailedRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOnRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mInCallVoicePrivacyOnRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
+ mInCallVoicePrivacyOffRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mInCallVoicePrivacyOffRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA call waiting comes
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCallWaiting(Handler h, int what, Object obj){
+ mCallWaitingRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA Call waiting comes
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCallWaiting(Handler h){
+ mCallWaitingRegistrants.remove(h);
+ }
+
+
+ /**
+ * Register for signal information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+
+ public void registerForSignalInfo(Handler h, int what, Object obj){
+ mSignalInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for signal information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSignalInfo(Handler h){
+ mSignalInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for display information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForDisplayInfo(Handler h, int what, Object obj){
+ mDisplayInfoRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for display information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForDisplayInfo(Handler h) {
+ mDisplayInfoRegistrants.remove(h);
+ }
+
+ /**
+ * Register for notifications when CDMA OTA Provision status change
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){
+ mCdmaOtaStatusChangeRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications when CDMA OTA Provision status change
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCdmaOtaStatusChange(Handler h){
+ mCdmaOtaStatusChangeRegistrants.remove(h);
+ }
+
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){
+ mSubscriptionInfoReadyRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h){
+ mSubscriptionInfoReadyRegistrants.remove(h);
+ }
+
+ /**
+ * Sets an event to be fired when the telephony system processes
+ * a post-dial character on an outgoing call.
+ *
+ * Messages of type what
will be sent to h
.
+ * The obj
field of these Message's will be instances of
+ * AsyncResult
. Message.obj.result
will be
+ * a Connection object.
+ *
+ * Message.arg1 will be the post dial character being processed,
+ * or 0 ('\0') if end of string.
+ *
+ * If Connection.getPostDialState() == WAIT,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar()
+ * Connection.proceedAfterWaitChar()} or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the post-dial
+ * DTMF sequence.
+ *
+ * If Connection.getPostDialState() == WILD,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWildChar
+ * Connection.proceedAfterWildChar()}
+ * or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the
+ * post-dial DTMF sequence.
+ *
+ */
+ public void registerForPostDialCharacter(Handler h, int what, Object obj){
+ mPostDialCharacterRegistrants.addUnique(h, what, obj);
+ }
+
+ public void unregisterForPostDialCharacter(Handler h){
+ mPostDialCharacterRegistrants.remove(h);
+ }
+
+ /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls
+ * 1. APIs to access list of calls
+ * 2. APIs to check if any active call, which has connection other than
+ * disconnected ones, pleaser refer to Call.isIdle()
+ * 3. APIs to return first active call
+ * 4. APIs to return the connections of first active call
+ * 5. APIs to return other property of first active call
+ */
+
+ /**
+ * @return list of all ringing calls
+ */
+ public List getRingingCalls() {
+ return Collections.unmodifiableList(mRingingCalls);
+ }
+
+ /**
+ * @return list of all foreground calls
+ */
+ public List getForegroundCalls() {
+ return Collections.unmodifiableList(mForegroundCalls);
+ }
+
+ /**
+ * @return list of all background calls
+ */
+ public List getBackgroundCalls() {
+ return Collections.unmodifiableList(mBackgroundCalls);
+ }
+
+ /**
+ * Return true if there is at least one active foreground call
+ */
+ public boolean hasActiveFgCall() {
+ return (getFirstActiveCall(mForegroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active background call
+ */
+ public boolean hasActiveBgCall() {
+ // TODO since hasActiveBgCall may get called often
+ // better to cache it to improve performance
+ return (getFirstActiveCall(mBackgroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active ringing call
+ *
+ */
+ public boolean hasActiveRingingCall() {
+ return (getFirstActiveCall(mRingingCalls) != null);
+ }
+
+ /**
+ * return the active foreground call from foreground calls
+ *
+ * Active call means the call is NOT in Call.State.IDLE
+ *
+ * 1. If there is active foreground call, return it
+ * 2. If there is no active foreground call, return the
+ * foreground call associated with default phone, which state is IDLE.
+ * 3. If there is no phone registered at all, return null.
+ *
+ */
+ public Call getActiveFgCall() {
+ Call call = getFirstNonIdleCall(mForegroundCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getForegroundCall();
+ }
+ return call;
+ }
+
+ // Returns the first call that is not in IDLE state. If both active calls
+ // and disconnecting/disconnected calls exist, return the first active call.
+ private Call getFirstNonIdleCall(List calls) {
+ Call result = null;
+ for (Call call : calls) {
+ if (!call.isIdle()) {
+ return call;
+ } else if (call.getState() != Call.State.IDLE) {
+ if (result == null) result = call;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * return one active background call from background calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active background call, return it
+ * 2. If there is more than one active background call, return the first one
+ * 3. If there is no active background call, return the background call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no background call at all, return null.
+ *
+ * Complete background calls list can be get by getBackgroundCalls()
+ */
+ public Call getFirstActiveBgCall() {
+ Call call = getFirstNonIdleCall(mBackgroundCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getBackgroundCall();
+ }
+ return call;
+ }
+
+ /**
+ * return one active ringing call from ringing calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active ringing call, return it
+ * 2. If there is more than one active ringing call, return the first one
+ * 3. If there is no active ringing call, return the ringing call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no ringing call at all, return null.
+ *
+ * Complete ringing calls list can be get by getRingingCalls()
+ */
+ public Call getFirstActiveRingingCall() {
+ Call call = getFirstNonIdleCall(mRingingCalls);
+ if (call == null) {
+ call = (mDefaultPhone == null)
+ ? null
+ : mDefaultPhone.getRingingCall();
+ }
+ return call;
+ }
+
+ /**
+ * @return the state of active foreground call
+ * return IDLE if there is no active foreground call
+ */
+ public Call.State getActiveFgCallState() {
+ Call fgCall = getActiveFgCall();
+
+ if (fgCall != null) {
+ return fgCall.getState();
+ }
+
+ return Call.State.IDLE;
+ }
+
+ /**
+ * @return the connections of active foreground call
+ * return empty list if there is no active foreground call
+ */
+ public List getFgCallConnections() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the connections of active background call
+ * return empty list if there is no active background call
+ */
+ public List getBgCallConnections() {
+ Call bgCall = getFirstActiveBgCall();
+ if ( bgCall != null) {
+ return bgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the latest connection of active foreground call
+ * return null if there is no active foreground call
+ */
+ public Connection getFgCallLatestConnection() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getLatestConnection();
+ }
+ return null;
+ }
+
+ /**
+ * @return true if there is at least one Foreground call in disconnected state
+ */
+ public boolean hasDisconnectedFgCall() {
+ return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return true if there is at least one background call in disconnected state
+ */
+ public boolean hasDisconnectedBgCall() {
+ return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return the first active call from a call list
+ */
+ private Call getFirstActiveCall(ArrayList calls) {
+ for (Call call : calls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the first call in a the Call.state from a call list
+ */
+ private Call getFirstCallOfState(ArrayList calls, Call.State state) {
+ for (Call call : calls) {
+ if (call.getState() == state) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+
+ private boolean hasMoreThanOneRingingCall() {
+ int count = 0;
+ for (Call call : mRingingCalls) {
+ if (call.getState().isRinging()) {
+ if (++count > 1) return true;
+ }
+ }
+ return false;
+ }
+
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ switch (msg.what) {
+ case EVENT_DISCONNECT:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
+ mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_PRECISE_CALL_STATE_CHANGED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
+ mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_NEW_RINGING_CONNECTION:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
+ if (getActiveFgCallState().isDialing() || hasMoreThanOneRingingCall()) {
+ Connection c = (Connection) ((AsyncResult) msg.obj).result;
+ try {
+ Log.d(LOG_TAG, "silently drop incoming call: " + c.getCall());
+ c.getCall().hangup();
+ } catch (CallStateException e) {
+ Log.w(LOG_TAG, "new ringing connection", e);
+ }
+ } else {
+ mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ }
+ break;
+ case EVENT_UNKNOWN_CONNECTION:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)");
+ mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_INCOMING_RING:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_INCOMING_RING)");
+ // The event may come from RIL who's not aware of an ongoing fg call
+ if (!hasActiveFgCall()) {
+ mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ }
+ break;
+ case EVENT_RINGBACK_TONE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RINGBACK_TONE)");
+ mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_ON:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_ON)");
+ mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_OFF)");
+ mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CALL_WAITING:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CALL_WAITING)");
+ mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_DISPLAY_INFO:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISPLAY_INFO)");
+ mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SIGNAL_INFO:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SIGNAL_INFO)");
+ mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CDMA_OTA_STATUS_CHANGE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CDMA_OTA_STATUS_CHANGE)");
+ mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_RESEND_INCALL_MUTE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RESEND_INCALL_MUTE)");
+ mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_INITIATE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_INITIATE)");
+ mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_MMI_COMPLETE:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
+ mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_ECM_TIMER_RESET:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_ECM_TIMER_RESET)");
+ mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUBSCRIPTION_INFO_READY:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUBSCRIPTION_INFO_READY)");
+ mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SUPP_SERVICE_FAILED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUPP_SERVICE_FAILED)");
+ mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SERVICE_STATE_CHANGED)");
+ mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_POST_DIAL_CHARACTER:
+ // we need send the character that is being processed in msg.arg1
+ // so can't use notifyRegistrants()
+ if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_POST_DIAL_CHARACTER)");
+ for(int i=0; i < mPostDialCharacterRegistrants.size(); i++) {
+ Message notifyMsg;
+ notifyMsg = ((Registrant)mPostDialCharacterRegistrants.get(i)).messageForRegistrant();
+ notifyMsg.obj = msg.obj;
+ notifyMsg.arg1 = msg.arg1;
+ notifyMsg.sendToTarget();
+ }
+ break;
+ }
+ }
+ };
+
+ @Override
+ public String toString() {
+ Call call;
+ StringBuilder b = new StringBuilder();
+
+ b.append("CallManager {");
+ b.append("\nstate = " + getState());
+ call = getActiveFgCall();
+ b.append("\n- Foreground: " + getActiveFgCallState());
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getFgCallConnections());
+ call = getFirstActiveBgCall();
+ b.append("\n- Background: " + call.getState());
+ b.append(" from " + call.getPhone());
+ b.append("\n Conn: ").append(getBgCallConnections());
+ call = getFirstActiveRingingCall();
+ b.append("\n- Ringing: " +call.getState());
+ b.append(" from " + call.getPhone());
+
+ for (Phone phone : getAllPhones()) {
+ if (phone != null) {
+ b.append("\nPhone: " + phone + ", name = " + phone.getPhoneName()
+ + ", state = " + phone.getState());
+ call = phone.getForegroundCall();
+ b.append("\n- Foreground: ").append(call);
+ call = phone.getBackgroundCall();
+ b.append(" Background: ").append(call);
+ call = phone.getRingingCall();
+ b.append(" Ringing: ").append(call);
+ }
+ }
+ b.append("\n}");
+ return b.toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
new file mode 100644
index 0000000000000000000000000000000000000000..60871248d3fb238f7342bf25b641f849d09ae019
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class CallStateException extends Exception
+{
+ public
+ CallStateException()
+ {
+ }
+
+ public
+ CallStateException(String string)
+ {
+ super(string);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..62caf01df60043fa83ce302fdee59987a1c85c91
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -0,0 +1,182 @@
+/*
+ * 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.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.CommandException;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+
+/**
+ * {@hide}
+ */
+public abstract class CallTracker extends Handler {
+
+ private static final boolean DBG_POLL = false;
+
+ //***** Constants
+
+ static final int POLL_DELAY_MSEC = 250;
+
+ protected int pendingOperations;
+ protected boolean needsPoll;
+ protected Message lastRelevantPoll;
+
+ public CommandsInterface cm;
+
+
+ //***** Events
+
+ protected static final int EVENT_POLL_CALLS_RESULT = 1;
+ protected static final int EVENT_CALL_STATE_CHANGE = 2;
+ protected static final int EVENT_REPOLL_AFTER_DELAY = 3;
+ protected static final int EVENT_OPERATION_COMPLETE = 4;
+ protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5;
+
+ protected static final int EVENT_SWITCH_RESULT = 8;
+ protected static final int EVENT_RADIO_AVAILABLE = 9;
+ protected static final int EVENT_RADIO_NOT_AVAILABLE = 10;
+ protected static final int EVENT_CONFERENCE_RESULT = 11;
+ protected static final int EVENT_SEPARATE_RESULT = 12;
+ protected static final int EVENT_ECT_RESULT = 13;
+ protected static final int EVENT_EXIT_ECM_RESPONSE_CDMA = 14;
+ protected static final int EVENT_CALL_WAITING_INFO_CDMA = 15;
+ protected static final int EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA = 16;
+
+ protected void pollCallsWhenSafe() {
+ needsPoll = true;
+
+ if (checkNoOperationsPending()) {
+ lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
+ cm.getCurrentCalls(lastRelevantPoll);
+ }
+ }
+
+ protected void
+ pollCallsAfterDelay() {
+ Message msg = obtainMessage();
+
+ msg.what = EVENT_REPOLL_AFTER_DELAY;
+ sendMessageDelayed(msg, POLL_DELAY_MSEC);
+ }
+
+ protected boolean
+ isCommandExceptionRadioNotAvailable(Throwable e) {
+ return e != null && e instanceof CommandException
+ && ((CommandException)e).getCommandError()
+ == CommandException.Error.RADIO_NOT_AVAILABLE;
+ }
+
+ protected abstract void handlePollCalls(AsyncResult ar);
+
+ protected void handleRadioAvailable() {
+ pollCallsWhenSafe();
+ }
+
+ /**
+ * Obtain a complete message that indicates that this operation
+ * does not require polling of getCurrentCalls(). However, if other
+ * operations that do need getCurrentCalls() are pending or are
+ * scheduled while this operation is pending, the invocation
+ * of getCurrentCalls() will be postponed until this
+ * operation is also complete.
+ */
+ protected Message
+ obtainNoPollCompleteMessage(int what) {
+ pendingOperations++;
+ lastRelevantPoll = null;
+ return obtainMessage(what);
+ }
+
+ /**
+ * @return true if we're idle or there's a call to getCurrentCalls() pending
+ * but nothing else
+ */
+ private boolean
+ checkNoOperationsPending() {
+ if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" +
+ pendingOperations);
+ return pendingOperations == 0;
+ }
+
+ /**
+ * Routine called from dial to check if the number is a test Emergency number
+ * and if so remap the number. This allows a short emergency number to be remapped
+ * to a regular number for testing how the frameworks handles emergency numbers
+ * without actually calling an emergency number.
+ *
+ * This is not a full test and is not a substitute for testing real emergency
+ * numbers but can be useful.
+ *
+ * To use this feature set a system property ril.test.emergencynumber to a pair of
+ * numbers separated by a colon. If the first number matches the number parameter
+ * this routine returns the second number. Example:
+ *
+ * ril.test.emergencynumber=112:1-123-123-45678
+ *
+ * To test Dial 112 take call then hang up on MO device to enter ECM
+ * see RIL#processSolicited RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND
+ *
+ * @param number to test if it should be remapped
+ * @return the same number or the remapped number.
+ */
+ protected String checkForTestEmergencyNumber(String dialString) {
+ String testEn = SystemProperties.get("ril.test.emergencynumber");
+ if (DBG_POLL) {
+ log("checkForTestEmergencyNumber: dialString=" + dialString +
+ " testEn=" + testEn);
+ }
+ if (!TextUtils.isEmpty(testEn)) {
+ String values[] = testEn.split(":");
+ log("checkForTestEmergencyNumber: values.length=" + values.length);
+ if (values.length == 2) {
+ if (values[0].equals(
+ android.telephony.PhoneNumberUtils.stripSeparators(dialString))) {
+ cm.testingEmergencyCall();
+ log("checkForTestEmergencyNumber: remap " +
+ dialString + " to " + values[1]);
+ dialString = values[1];
+ }
+ }
+ }
+ return dialString;
+ }
+
+ //***** Overridden from Handler
+ public abstract void handleMessage (Message msg);
+ public abstract void registerForVoiceCallStarted(Handler h, int what, Object obj);
+ public abstract void unregisterForVoiceCallStarted(Handler h);
+ public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj);
+ public abstract void unregisterForVoiceCallEnded(Handler h);
+
+ protected abstract void log(String msg);
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CallTracker:");
+ pw.println(" pendingOperations=" + pendingOperations);
+ pw.println(" needsPoll=" + needsPoll);
+ pw.println(" lastRelevantPoll=" + lastRelevantPoll);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
new file mode 100644
index 0000000000000000000000000000000000000000..94c544ecffd5c731c7bc1bcdd3e117e3253380a0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.RILConstants;
+
+import android.util.Log;
+
+/**
+ * {@hide}
+ */
+public class CommandException extends RuntimeException {
+ private Error e;
+
+ public enum Error {
+ INVALID_RESPONSE,
+ RADIO_NOT_AVAILABLE,
+ GENERIC_FAILURE,
+ PASSWORD_INCORRECT,
+ SIM_PIN2,
+ SIM_PUK2,
+ REQUEST_NOT_SUPPORTED,
+ OP_NOT_ALLOWED_DURING_VOICE_CALL,
+ OP_NOT_ALLOWED_BEFORE_REG_NW,
+ SMS_FAIL_RETRY,
+ SIM_ABSENT,
+ SUBSCRIPTION_NOT_AVAILABLE,
+ MODE_NOT_SUPPORTED,
+ FDN_CHECK_FAILURE,
+ ILLEGAL_SIM_OR_ME,
+ }
+
+ public CommandException(Error e) {
+ super(e.toString());
+ this.e = e;
+ }
+
+ public static CommandException
+ fromRilErrno(int ril_errno) {
+ switch(ril_errno) {
+ case RILConstants.SUCCESS: return null;
+ case RILConstants.RIL_ERRNO_INVALID_RESPONSE:
+ return new CommandException(Error.INVALID_RESPONSE);
+ case RILConstants.RADIO_NOT_AVAILABLE:
+ return new CommandException(Error.RADIO_NOT_AVAILABLE);
+ case RILConstants.GENERIC_FAILURE:
+ return new CommandException(Error.GENERIC_FAILURE);
+ case RILConstants.PASSWORD_INCORRECT:
+ return new CommandException(Error.PASSWORD_INCORRECT);
+ case RILConstants.SIM_PIN2:
+ return new CommandException(Error.SIM_PIN2);
+ case RILConstants.SIM_PUK2:
+ return new CommandException(Error.SIM_PUK2);
+ case RILConstants.REQUEST_NOT_SUPPORTED:
+ return new CommandException(Error.REQUEST_NOT_SUPPORTED);
+ case RILConstants.OP_NOT_ALLOWED_DURING_VOICE_CALL:
+ return new CommandException(Error.OP_NOT_ALLOWED_DURING_VOICE_CALL);
+ case RILConstants.OP_NOT_ALLOWED_BEFORE_REG_NW:
+ return new CommandException(Error.OP_NOT_ALLOWED_BEFORE_REG_NW);
+ case RILConstants.SMS_SEND_FAIL_RETRY:
+ return new CommandException(Error.SMS_FAIL_RETRY);
+ case RILConstants.SIM_ABSENT:
+ return new CommandException(Error.SIM_ABSENT);
+ case RILConstants.SUBSCRIPTION_NOT_AVAILABLE:
+ return new CommandException(Error.SUBSCRIPTION_NOT_AVAILABLE);
+ case RILConstants.MODE_NOT_SUPPORTED:
+ return new CommandException(Error.MODE_NOT_SUPPORTED);
+ case RILConstants.FDN_CHECK_FAILURE:
+ return new CommandException(Error.FDN_CHECK_FAILURE);
+ case RILConstants.ILLEGAL_SIM_OR_ME:
+ return new CommandException(Error.ILLEGAL_SIM_OR_ME);
+ default:
+ Log.e("GSM", "Unrecognized RIL errno " + ril_errno);
+ return new CommandException(Error.INVALID_RESPONSE);
+ }
+ }
+
+ public Error getCommandError() {
+ return e;
+ }
+
+
+
+}
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7757b3867e5ea6ca7f0ec53309d0b4fde4f8d18
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -0,0 +1,1579 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+
+import android.os.Message;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * {@hide}
+ */
+public interface CommandsInterface {
+ enum RadioState {
+ RADIO_OFF, /* Radio explicitly powered off (eg CFUN=0) */
+ RADIO_UNAVAILABLE, /* Radio unavailable (eg, resetting or not booted) */
+ RADIO_ON; /* Radio is on */
+
+ public boolean isOn() /* and available...*/ {
+ return this == RADIO_ON;
+ }
+
+ public boolean isAvailable() {
+ return this != RADIO_UNAVAILABLE;
+ }
+ }
+
+ //***** Constants
+
+ // Used as parameter to dial() and setCLIR() below
+ static final int CLIR_DEFAULT = 0; // "use subscription default value"
+ static final int CLIR_INVOCATION = 1; // (restrict CLI presentation)
+ static final int CLIR_SUPPRESSION = 2; // (allow CLI presentation)
+
+
+ // Used as parameters for call forward methods below
+ static final int CF_ACTION_DISABLE = 0;
+ static final int CF_ACTION_ENABLE = 1;
+// static final int CF_ACTION_UNUSED = 2;
+ static final int CF_ACTION_REGISTRATION = 3;
+ static final int CF_ACTION_ERASURE = 4;
+
+ static final int CF_REASON_UNCONDITIONAL = 0;
+ static final int CF_REASON_BUSY = 1;
+ static final int CF_REASON_NO_REPLY = 2;
+ static final int CF_REASON_NOT_REACHABLE = 3;
+ static final int CF_REASON_ALL = 4;
+ static final int CF_REASON_ALL_CONDITIONAL = 5;
+
+ // Used for call barring methods below
+ static final String CB_FACILITY_BAOC = "AO";
+ static final String CB_FACILITY_BAOIC = "OI";
+ static final String CB_FACILITY_BAOICxH = "OX";
+ static final String CB_FACILITY_BAIC = "AI";
+ static final String CB_FACILITY_BAICr = "IR";
+ static final String CB_FACILITY_BA_ALL = "AB";
+ static final String CB_FACILITY_BA_MO = "AG";
+ static final String CB_FACILITY_BA_MT = "AC";
+ static final String CB_FACILITY_BA_SIM = "SC";
+ static final String CB_FACILITY_BA_FD = "FD";
+
+
+ // Used for various supp services apis
+ // See 27.007 +CCFC or +CLCK
+ static final int SERVICE_CLASS_NONE = 0; // no user input
+ static final int SERVICE_CLASS_VOICE = (1 << 0);
+ static final int SERVICE_CLASS_DATA = (1 << 1); //synonym for 16+32+64+128
+ static final int SERVICE_CLASS_FAX = (1 << 2);
+ static final int SERVICE_CLASS_SMS = (1 << 3);
+ static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
+ static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5);
+ static final int SERVICE_CLASS_PACKET = (1 << 6);
+ static final int SERVICE_CLASS_PAD = (1 << 7);
+ static final int SERVICE_CLASS_MAX = (1 << 7); // Max SERVICE_CLASS value
+
+ // Numeric representation of string values returned
+ // by messages sent to setOnUSSD handler
+ static final int USSD_MODE_NOTIFY = 0;
+ static final int USSD_MODE_REQUEST = 1;
+
+ // GSM SMS fail cause for acknowledgeLastIncomingSMS. From TS 23.040, 9.2.3.22.
+ static final int GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED = 0xD3;
+ static final int GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY = 0xD4;
+ static final int GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR = 0xD5;
+ static final int GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR = 0xFF;
+
+ // CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S0005, 6.5.2.125.
+ static final int CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID = 4;
+ static final int CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE = 35;
+ static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39;
+ static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM = 96;
+
+ //***** Methods
+ RadioState getRadioState();
+
+ void getVoiceRadioTechnology(Message result);
+
+ /**
+ * Fires on any RadioState transition
+ * Always fires immediately as well
+ *
+ * do not attempt to calculate transitions by storing getRadioState() values
+ * on previous invocations of this notification. Instead, use the other
+ * registration methods
+ */
+ void registerForRadioStateChanged(Handler h, int what, Object obj);
+ void unregisterForRadioStateChanged(Handler h);
+
+ void registerForVoiceRadioTechChanged(Handler h, int what, Object obj);
+ void unregisterForVoiceRadioTechChanged(Handler h);
+
+ /**
+ * Fires on any transition into RadioState.isOn()
+ * Fires immediately if currently in that state
+ * In general, actions should be idempotent. State may change
+ * before event is received.
+ */
+ void registerForOn(Handler h, int what, Object obj);
+ void unregisterForOn(Handler h);
+
+ /**
+ * Fires on any transition out of RadioState.isAvailable()
+ * Fires immediately if currently in that state
+ * In general, actions should be idempotent. State may change
+ * before event is received.
+ */
+ void registerForAvailable(Handler h, int what, Object obj);
+ void unregisterForAvailable(Handler h);
+
+ /**
+ * Fires on any transition into !RadioState.isAvailable()
+ * Fires immediately if currently in that state
+ * In general, actions should be idempotent. State may change
+ * before event is received.
+ */
+ void registerForNotAvailable(Handler h, int what, Object obj);
+ void unregisterForNotAvailable(Handler h);
+
+ /**
+ * Fires on any transition into RADIO_OFF or !RadioState.isAvailable()
+ * Fires immediately if currently in that state
+ * In general, actions should be idempotent. State may change
+ * before event is received.
+ */
+ void registerForOffOrNotAvailable(Handler h, int what, Object obj);
+ void unregisterForOffOrNotAvailable(Handler h);
+
+ /**
+ * Fires on any change in ICC status
+ */
+ void registerForIccStatusChanged(Handler h, int what, Object obj);
+ void unregisterForIccStatusChanged(Handler h);
+
+ void registerForCallStateChanged(Handler h, int what, Object obj);
+ void unregisterForCallStateChanged(Handler h);
+ void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj);
+ void unregisterForVoiceNetworkStateChanged(Handler h);
+ void registerForDataNetworkStateChanged(Handler h, int what, Object obj);
+ void unregisterForDataNetworkStateChanged(Handler h);
+
+ /** InCall voice privacy notifications */
+ void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj);
+ void unregisterForInCallVoicePrivacyOn(Handler h);
+ void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj);
+ void unregisterForInCallVoicePrivacyOff(Handler h);
+
+ /**
+ * unlike the register* methods, there's only one new 3GPP format SMS handler.
+ * if you need to unregister, you should also tell the radio to stop
+ * sending SMS's to you (via AT+CNMI)
+ *
+ * AsyncResult.result is a String containing the SMS PDU
+ */
+ void setOnNewGsmSms(Handler h, int what, Object obj);
+ void unSetOnNewGsmSms(Handler h);
+
+ /**
+ * unlike the register* methods, there's only one new 3GPP2 format SMS handler.
+ * if you need to unregister, you should also tell the radio to stop
+ * sending SMS's to you (via AT+CNMI)
+ *
+ * AsyncResult.result is a String containing the SMS PDU
+ */
+ void setOnNewCdmaSms(Handler h, int what, Object obj);
+ void unSetOnNewCdmaSms(Handler h);
+
+ /**
+ * Set the handler for SMS Cell Broadcast messages.
+ *
+ * AsyncResult.result is a byte array containing the SMS-CB PDU
+ */
+ void setOnNewGsmBroadcastSms(Handler h, int what, Object obj);
+ void unSetOnNewGsmBroadcastSms(Handler h);
+
+ /**
+ * Register for NEW_SMS_ON_SIM unsolicited message
+ *
+ * AsyncResult.result is an int array containing the index of new SMS
+ */
+ void setOnSmsOnSim(Handler h, int what, Object obj);
+ void unSetOnSmsOnSim(Handler h);
+
+ /**
+ * Register for NEW_SMS_STATUS_REPORT unsolicited message
+ *
+ * AsyncResult.result is a String containing the status report PDU
+ */
+ void setOnSmsStatus(Handler h, int what, Object obj);
+ void unSetOnSmsStatus(Handler h);
+
+ /**
+ * unlike the register* methods, there's only one NITZ time handler
+ *
+ * AsyncResult.result is an Object[]
+ * ((Object[])AsyncResult.result)[0] is a String containing the NITZ time string
+ * ((Object[])AsyncResult.result)[1] is a Long containing the milliseconds since boot as
+ * returned by elapsedRealtime() when this NITZ time
+ * was posted.
+ *
+ * Please note that the delivery of this message may be delayed several
+ * seconds on system startup
+ */
+ void setOnNITZTime(Handler h, int what, Object obj);
+ void unSetOnNITZTime(Handler h);
+
+ /**
+ * unlike the register* methods, there's only one USSD notify handler
+ *
+ * Represents the arrival of a USSD "notify" message, which may
+ * or may not have been triggered by a previous USSD send
+ *
+ * AsyncResult.result is a String[]
+ * ((String[])(AsyncResult.result))[0] contains status code
+ * "0" USSD-Notify -- text in ((const char **)data)[1]
+ * "1" USSD-Request -- text in ((const char **)data)[1]
+ * "2" Session terminated by network
+ * "3" other local client (eg, SIM Toolkit) has responded
+ * "4" Operation not supported
+ * "5" Network timeout
+ *
+ * ((String[])(AsyncResult.result))[1] contains the USSD message
+ * The numeric representations of these are in USSD_MODE_*
+ */
+
+ void setOnUSSD(Handler h, int what, Object obj);
+ void unSetOnUSSD(Handler h);
+
+ /**
+ * unlike the register* methods, there's only one signal strength handler
+ * AsyncResult.result is an int[2]
+ * response.obj.result[0] is received signal strength (0-31, 99)
+ * response.obj.result[1] is bit error rate (0-7, 99)
+ * as defined in TS 27.007 8.5
+ */
+
+ void setOnSignalStrengthUpdate(Handler h, int what, Object obj);
+ void unSetOnSignalStrengthUpdate(Handler h);
+
+ /**
+ * Sets the handler for SIM/RUIM SMS storage full unsolicited message.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnIccSmsFull(Handler h, int what, Object obj);
+ void unSetOnIccSmsFull(Handler h);
+
+ /**
+ * Sets the handler for SIM Refresh notifications.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForIccRefresh(Handler h, int what, Object obj);
+ void unregisterForIccRefresh(Handler h);
+
+ void setOnIccRefresh(Handler h, int what, Object obj);
+ void unsetOnIccRefresh(Handler h);
+
+ /**
+ * Sets the handler for RING notifications.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnCallRing(Handler h, int what, Object obj);
+ void unSetOnCallRing(Handler h);
+
+ /**
+ * Sets the handler for RESTRICTED_STATE changed notification,
+ * eg, for Domain Specific Access Control
+ * unlike the register* methods, there's only one signal strength handler
+ *
+ * AsyncResult.result is an int[1]
+ * response.obj.result[0] is a bitmask of RIL_RESTRICTED_STATE_* values
+ */
+
+ void setOnRestrictedStateChanged(Handler h, int what, Object obj);
+ void unSetOnRestrictedStateChanged(Handler h);
+
+ /**
+ * Sets the handler for Supplementary Service Notifications.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnSuppServiceNotification(Handler h, int what, Object obj);
+ void unSetOnSuppServiceNotification(Handler h);
+
+ /**
+ * Sets the handler for Session End Notifications for CAT.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnCatSessionEnd(Handler h, int what, Object obj);
+ void unSetOnCatSessionEnd(Handler h);
+
+ /**
+ * Sets the handler for Proactive Commands for CAT.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnCatProactiveCmd(Handler h, int what, Object obj);
+ void unSetOnCatProactiveCmd(Handler h);
+
+ /**
+ * Sets the handler for Event Notifications for CAT.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnCatEvent(Handler h, int what, Object obj);
+ void unSetOnCatEvent(Handler h);
+
+ /**
+ * Sets the handler for Call Set Up Notifications for CAT.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void setOnCatCallSetUp(Handler h, int what, Object obj);
+ void unSetOnCatCallSetUp(Handler h);
+
+ /**
+ * Enables/disbables supplementary service related notifications from
+ * the network.
+ *
+ * @param enable true to enable notifications, false to disable.
+ * @param result Message to be posted when command completes.
+ */
+ void setSuppServiceNotifications(boolean enable, Message result);
+ //void unSetSuppServiceNotifications(Handler h);
+
+ /**
+ * Sets the handler for Event Notifications for CDMA Display Info.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForDisplayInfo(Handler h, int what, Object obj);
+ void unregisterForDisplayInfo(Handler h);
+
+ /**
+ * Sets the handler for Event Notifications for CallWaiting Info.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForCallWaitingInfo(Handler h, int what, Object obj);
+ void unregisterForCallWaitingInfo(Handler h);
+
+ /**
+ * Sets the handler for Event Notifications for Signal Info.
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForSignalInfo(Handler h, int what, Object obj);
+ void unregisterForSignalInfo(Handler h);
+
+ /**
+ * Registers the handler for CDMA number information record
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForNumberInfo(Handler h, int what, Object obj);
+ void unregisterForNumberInfo(Handler h);
+
+ /**
+ * Registers the handler for CDMA redirected number Information record
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForRedirectedNumberInfo(Handler h, int what, Object obj);
+ void unregisterForRedirectedNumberInfo(Handler h);
+
+ /**
+ * Registers the handler for CDMA line control information record
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForLineControlInfo(Handler h, int what, Object obj);
+ void unregisterForLineControlInfo(Handler h);
+
+ /**
+ * Registers the handler for CDMA T53 CLIR information record
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerFoT53ClirlInfo(Handler h, int what, Object obj);
+ void unregisterForT53ClirInfo(Handler h);
+
+ /**
+ * Registers the handler for CDMA T53 audio control information record
+ * Unlike the register* methods, there's only one notification handler
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForT53AudioControlInfo(Handler h, int what, Object obj);
+ void unregisterForT53AudioControlInfo(Handler h);
+
+ /**
+ * Fires on if Modem enters Emergency Callback mode
+ */
+ void setEmergencyCallbackMode(Handler h, int what, Object obj);
+
+ /**
+ * Fires on any CDMA OTA provision status change
+ */
+ void registerForCdmaOtaProvision(Handler h,int what, Object obj);
+ void unregisterForCdmaOtaProvision(Handler h);
+
+ /**
+ * Registers the handler when out-band ringback tone is needed.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean.
+ */
+ void registerForRingbackTone(Handler h, int what, Object obj);
+ void unregisterForRingbackTone(Handler h);
+
+ /**
+ * Registers the handler when mute/unmute need to be resent to get
+ * uplink audio during a call.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForResendIncallMute(Handler h, int what, Object obj);
+ void unregisterForResendIncallMute(Handler h);
+
+ /**
+ * Registers the handler for when Cdma subscription changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj);
+ void unregisterForCdmaSubscriptionChanged(Handler h);
+
+ /**
+ * Registers the handler for when Cdma prl changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForCdmaPrlChanged(Handler h, int what, Object obj);
+ void unregisterForCdmaPrlChanged(Handler h);
+
+ /**
+ * Registers the handler for when Cdma prl changed events
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ *
+ */
+ void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj);
+ void unregisterForExitEmergencyCallbackMode(Handler h);
+
+ /**
+ * Registers the handler for RIL_UNSOL_RIL_CONNECT events.
+ *
+ * When ril connects or disconnects a message is sent to the registrant
+ * which contains an AsyncResult, ar, in msg.obj. The ar.result is an
+ * Integer which is the version of the ril or -1 if the ril disconnected.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForRilConnected(Handler h, int what, Object obj);
+ void unregisterForRilConnected(Handler h);
+
+ /**
+ * Supply the ICC PIN to the ICC card
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPin(String pin, Message result);
+
+ /**
+ * Supply the PIN for the app with this AID on the ICC card
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPinForApp(String pin, String aid, Message result);
+
+ /**
+ * Supply the ICC PUK and newPin to the ICC card
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPuk(String puk, String newPin, Message result);
+
+ /**
+ * Supply the PUK, new pin for the app with this AID on the ICC card
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPukForApp(String puk, String newPin, String aid, Message result);
+
+ /**
+ * Supply the ICC PIN2 to the ICC card
+ * Only called following operation where ICC_PIN2 was
+ * returned as a a failure from a previous operation
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPin2(String pin2, Message result);
+
+ /**
+ * Supply the PIN2 for the app with this AID on the ICC card
+ * Only called following operation where ICC_PIN2 was
+ * returned as a a failure from a previous operation
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPin2ForApp(String pin2, String aid, Message result);
+
+ /**
+ * Supply the SIM PUK2 to the SIM card
+ * Only called following operation where SIM_PUK2 was
+ * returned as a a failure from a previous operation
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPuk2(String puk2, String newPin2, Message result);
+
+ /**
+ * Supply the PUK2, newPin2 for the app with this AID on the ICC card
+ * Only called following operation where SIM_PUK2 was
+ * returned as a a failure from a previous operation
+ *
+ * AID (Application ID), See ETSI 102.221 8.1 and 101.220 4
+ *
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * This exception is CommandException with an error of PASSWORD_INCORRECT
+ * if the password is incorrect
+ *
+ * ar.exception and ar.result are null on success
+ */
+
+ void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result);
+
+ void changeIccPin(String oldPin, String newPin, Message result);
+ void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result);
+ void changeIccPin2(String oldPin2, String newPin2, Message result);
+ void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr, Message result);
+
+ void changeBarringPassword(String facility, String oldPwd, String newPwd, Message result);
+
+ void supplyNetworkDepersonalization(String netpin, Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result contains a List of DriverCall
+ * The ar.result List is sorted by DriverCall.index
+ */
+ void getCurrentCalls (Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result contains a List of DataCallState
+ * @deprecated Do not use.
+ */
+ @Deprecated
+ void getPDPContextList(Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result contains a List of DataCallState
+ */
+ void getDataCallList(Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ *
+ * CLIR_DEFAULT == on "use subscription default value"
+ * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation)
+ * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation)
+ */
+ void dial (String address, int clirMode, Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ *
+ * CLIR_DEFAULT == on "use subscription default value"
+ * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation)
+ * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation)
+ */
+ void dial(String address, int clirMode, UUSInfo uusInfo, Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is String containing IMSI on success
+ */
+ void getIMSI(Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is String containing IMSI on success
+ */
+ void getIMSIForApp(String aid, Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is String containing IMEI on success
+ */
+ void getIMEI(Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is String containing IMEISV on success
+ */
+ void getIMEISV(Message result);
+
+ /**
+ * Hang up one individual connection.
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ *
+ * 3GPP 22.030 6.5.5
+ * "Releases a specific active call X"
+ */
+ void hangupConnection (int gsmIndex, Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Releases all held calls or sets User Determined User Busy (UDUB)
+ * for a waiting call."
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void hangupWaitingOrBackground (Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Releases all active calls (if any exist) and accepts
+ * the other (held or waiting) call."
+ *
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void hangupForegroundResumeBackground (Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Places all active calls (if any exist) on hold and accepts
+ * the other (held or waiting) call."
+ *
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void switchWaitingOrHoldingAndActive (Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Adds a held call to the conversation"
+ *
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void conference (Message result);
+
+ /**
+ * Set preferred Voice Privacy (VP).
+ *
+ * @param enable true is enhanced and false is normal VP
+ * @param result is a callback message
+ */
+ void setPreferredVoicePrivacy(boolean enable, Message result);
+
+ /**
+ * Get currently set preferred Voice Privacy (VP) mode.
+ *
+ * @param result is a callback message
+ */
+ void getPreferredVoicePrivacy(Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Places all active calls on hold except call X with which
+ * communication shall be supported."
+ */
+ void separateConnection (int gsmIndex, Message result);
+
+ /**
+ *
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void acceptCall (Message result);
+
+ /**
+ * also known as UDUB
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void rejectCall (Message result);
+
+ /**
+ * 3GPP 22.030 6.5.5
+ * "Connects the two calls and disconnects the subscriber from both calls"
+ *
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void explicitCallTransfer (Message result);
+
+ /**
+ * cause code returned as int[0] in Message.obj.response
+ * Returns integer cause code defined in TS 24.008
+ * Annex H or closest approximation.
+ * Most significant codes:
+ * - Any defined in 22.001 F.4 (for generating busy/congestion)
+ * - Cause 68: ACM >= ACMMax
+ */
+ void getLastCallFailCause (Message result);
+
+
+ /**
+ * Reason for last PDP context deactivate or failure to activate
+ * cause code returned as int[0] in Message.obj.response
+ * returns an integer cause code defined in TS 24.008
+ * section 6.1.3.1.3 or close approximation
+ * @deprecated Do not use.
+ */
+ @Deprecated
+ void getLastPdpFailCause (Message result);
+
+ /**
+ * The preferred new alternative to getLastPdpFailCause
+ * that is also CDMA-compatible.
+ */
+ void getLastDataCallFailCause (Message result);
+
+ void setMute (boolean enableMute, Message response);
+
+ void getMute (Message response);
+
+ /**
+ * response.obj is an AsyncResult
+ * response.obj.result is an int[2]
+ * response.obj.result[0] is received signal strength (0-31, 99)
+ * response.obj.result[1] is bit error rate (0-7, 99)
+ * as defined in TS 27.007 8.5
+ */
+ void getSignalStrength (Message response);
+
+
+ /**
+ * response.obj.result is an int[3]
+ * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2
+ * response.obj.result[1] is LAC if registered or -1 if not
+ * response.obj.result[2] is CID if registered or -1 if not
+ * valid LAC and CIDs are 0x0000 - 0xffff
+ *
+ * Please note that registration state 4 ("unknown") is treated
+ * as "out of service" above
+ */
+ void getVoiceRegistrationState (Message response);
+
+ /**
+ * response.obj.result is an int[3]
+ * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2
+ * response.obj.result[1] is LAC if registered or -1 if not
+ * response.obj.result[2] is CID if registered or -1 if not
+ * valid LAC and CIDs are 0x0000 - 0xffff
+ *
+ * Please note that registration state 4 ("unknown") is treated
+ * as "out of service" above
+ */
+ void getDataRegistrationState (Message response);
+
+ /**
+ * response.obj.result is a String[3]
+ * response.obj.result[0] is long alpha or null if unregistered
+ * response.obj.result[1] is short alpha or null if unregistered
+ * response.obj.result[2] is numeric or null if unregistered
+ */
+ void getOperator(Message response);
+
+ /**
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void sendDtmf(char c, Message result);
+
+
+ /**
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void startDtmf(char c, Message result);
+
+ /**
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void stopDtmf(Message result);
+
+ /**
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
+ * ar.result is null on success and failure
+ */
+ void sendBurstDtmf(String dtmfString, int on, int off, Message result);
+
+ /**
+ * smscPDU is smsc address in PDU form GSM BCD format prefixed
+ * by a length byte (as expected by TS 27.005) or NULL for default SMSC
+ * pdu is SMS in PDU format as an ASCII hex string
+ * less the SMSC address
+ */
+ void sendSMS (String smscPDU, String pdu, Message response);
+
+ /**
+ * @param pdu is CDMA-SMS in internal pseudo-PDU format
+ * @param response sent when operation completes
+ */
+ void sendCdmaSms(byte[] pdu, Message response);
+
+ /**
+ * Deletes the specified SMS record from SIM memory (EF_SMS).
+ *
+ * @param index index of the SMS record to delete
+ * @param response sent when operation completes
+ */
+ void deleteSmsOnSim(int index, Message response);
+
+ /**
+ * Deletes the specified SMS record from RUIM memory (EF_SMS in DF_CDMA).
+ *
+ * @param index index of the SMS record to delete
+ * @param response sent when operation completes
+ */
+ void deleteSmsOnRuim(int index, Message response);
+
+ /**
+ * Writes an SMS message to SIM memory (EF_SMS).
+ *
+ * @param status status of message on SIM. One of:
+ * SmsManger.STATUS_ON_ICC_READ
+ * SmsManger.STATUS_ON_ICC_UNREAD
+ * SmsManger.STATUS_ON_ICC_SENT
+ * SmsManger.STATUS_ON_ICC_UNSENT
+ * @param pdu message PDU, as hex string
+ * @param response sent when operation completes.
+ * response.obj will be an AsyncResult, and will indicate
+ * any error that may have occurred (eg, out of memory).
+ */
+ void writeSmsToSim(int status, String smsc, String pdu, Message response);
+
+ void writeSmsToRuim(int status, String pdu, Message response);
+
+ void setRadioPower(boolean on, Message response);
+
+ void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response);
+
+ void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response);
+
+ /**
+ * Acknowledge successful or failed receipt of last incoming SMS,
+ * including acknowledgement TPDU to send as the RP-User-Data element
+ * of the RP-ACK or RP-ERROR PDU.
+ *
+ * @param success true to send RP-ACK, false to send RP-ERROR
+ * @param ackPdu the acknowledgement TPDU in hexadecimal format
+ * @param response sent when operation completes.
+ */
+ void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message response);
+
+ /**
+ * parameters equivalent to 27.007 AT+CRSM command
+ * response.obj will be an AsyncResult
+ * response.obj.result will be an IccIoResult on success
+ */
+ void iccIO (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, Message response);
+
+ /**
+ * parameters equivalent to 27.007 AT+CRSM command
+ * response.obj will be an AsyncResult
+ * response.obj.userObj will be a IccIoResult on success
+ */
+ void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, String aid, Message response);
+
+ /**
+ * (AsyncResult)response.obj).result is an int[] with element [0] set to
+ * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned".
+ *
+ * @param response is callback message
+ */
+
+ void queryCLIP(Message response);
+
+ /**
+ * response.obj will be a an int[2]
+ *
+ * response.obj[0] will be TS 27.007 +CLIR parameter 'n'
+ * 0 presentation indicator is used according to the subscription of the CLIR service
+ * 1 CLIR invocation
+ * 2 CLIR suppression
+ *
+ * response.obj[1] will be TS 27.007 +CLIR parameter 'm'
+ * 0 CLIR not provisioned
+ * 1 CLIR provisioned in permanent mode
+ * 2 unknown (e.g. no network, etc.)
+ * 3 CLIR temporary mode presentation restricted
+ * 4 CLIR temporary mode presentation allowed
+ */
+
+ void getCLIR(Message response);
+
+ /**
+ * clirMode is one of the CLIR_* constants above
+ *
+ * response.obj is null
+ */
+
+ void setCLIR(int clirMode, Message response);
+
+ /**
+ * (AsyncResult)response.obj).result is an int[] with element [0] set to
+ * 0 for disabled, 1 for enabled.
+ *
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param response is callback message
+ */
+
+ void queryCallWaiting(int serviceClass, Message response);
+
+ /**
+ * @param enable is true to enable, false to disable
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param response is callback message
+ */
+
+ void setCallWaiting(boolean enable, int serviceClass, Message response);
+
+ /**
+ * @param action is one of CF_ACTION_*
+ * @param cfReason is one of CF_REASON_*
+ * @param serviceClass is a sum of SERVICE_CLASSS_*
+ */
+ void setCallForward(int action, int cfReason, int serviceClass,
+ String number, int timeSeconds, Message response);
+
+ /**
+ * cfReason is one of CF_REASON_*
+ *
+ * ((AsyncResult)response.obj).result will be an array of
+ * CallForwardInfo's
+ *
+ * An array of length 0 means "disabled for all codes"
+ */
+ void queryCallForwardStatus(int cfReason, int serviceClass,
+ String number, Message response);
+
+ void setNetworkSelectionModeAutomatic(Message response);
+
+ void setNetworkSelectionModeManual(String operatorNumeric, Message response);
+
+ /**
+ * Queries whether the current network selection mode is automatic
+ * or manual
+ *
+ * ((AsyncResult)response.obj).result is an int[] with element [0] being
+ * a 0 for automatic selection and a 1 for manual selection
+ */
+
+ void getNetworkSelectionMode(Message response);
+
+ /**
+ * Queries the currently available networks
+ *
+ * ((AsyncResult)response.obj).result is a List of NetworkInfo objects
+ */
+ void getAvailableNetworks(Message response);
+
+ void getBasebandVersion (Message response);
+
+
+ /**
+ * (AsyncResult)response.obj).result will be an Integer representing
+ * the sum of enabled service classes (sum of SERVICE_CLASS_*)
+ *
+ * @param facility one of CB_FACILTY_*
+ * @param password password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param response is callback message
+ */
+
+ void queryFacilityLock (String facility, String password, int serviceClass,
+ Message response);
+
+ /**
+ * (AsyncResult)response.obj).result will be an Integer representing
+ * the sum of enabled service classes (sum of SERVICE_CLASS_*) for the
+ * application with appId.
+ *
+ * @param facility one of CB_FACILTY_*
+ * @param password password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param appId is application Id or null if none
+ * @param response is callback message
+ */
+
+ void queryFacilityLockForApp(String facility, String password, int serviceClass, String appId,
+ Message response);
+
+ /**
+ * @param facility one of CB_FACILTY_*
+ * @param lockState true means lock, false means unlock
+ * @param password password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param response is callback message
+ */
+ void setFacilityLock (String facility, boolean lockState, String password,
+ int serviceClass, Message response);
+
+ /**
+ * Set the facility lock for the app with this AID on the ICC card.
+ *
+ * @param facility one of CB_FACILTY_*
+ * @param lockState true means lock, false means unlock
+ * @param password password or "" if not required
+ * @param serviceClass is a sum of SERVICE_CLASS_*
+ * @param appId is application Id or null if none
+ * @param response is callback message
+ */
+ void setFacilityLockForApp(String facility, boolean lockState, String password,
+ int serviceClass, String appId, Message response);
+
+ void sendUSSD (String ussdString, Message response);
+
+ /**
+ * Cancels a pending USSD session if one exists.
+ * @param response callback message
+ */
+ void cancelPendingUssd (Message response);
+
+ void resetRadio(Message result);
+
+ /**
+ * Assign a specified band for RF configuration.
+ *
+ * @param bandMode one of BM_*_BAND
+ * @param response is callback message
+ */
+ void setBandMode (int bandMode, Message response);
+
+ /**
+ * Query the list of band mode supported by RF.
+ *
+ * @param response is callback message
+ * ((AsyncResult)response.obj).result is an int[] with every
+ * element representing one avialable BM_*_BAND
+ */
+ void queryAvailableBandMode (Message response);
+
+ /**
+ * Set the current preferred network type. This will be the last
+ * networkType that was passed to setPreferredNetworkType.
+ */
+ void setCurrentPreferredNetworkType();
+
+ /**
+ * Requests to set the preferred network type for searching and registering
+ * (CS/PS domain, RAT, and operation mode)
+ * @param networkType one of NT_*_TYPE
+ * @param response is callback message
+ */
+ void setPreferredNetworkType(int networkType , Message response);
+
+ /**
+ * Query the preferred network type setting
+ *
+ * @param response is callback message to report one of NT_*_TYPE
+ */
+ void getPreferredNetworkType(Message response);
+
+ /**
+ * Query neighboring cell ids
+ *
+ * @param response s callback message to cell ids
+ */
+ void getNeighboringCids(Message response);
+
+ /**
+ * Request to enable/disable network state change notifications when
+ * location information (lac and/or cid) has changed.
+ *
+ * @param enable true to enable, false to disable
+ * @param response callback message
+ */
+ void setLocationUpdates(boolean enable, Message response);
+
+ /**
+ * Gets the default SMSC address.
+ *
+ * @param result Callback message contains the SMSC address.
+ */
+ void getSmscAddress(Message result);
+
+ /**
+ * Sets the default SMSC address.
+ *
+ * @param address new SMSC address
+ * @param result Callback message is empty on completion
+ */
+ void setSmscAddress(String address, Message result);
+
+ /**
+ * Indicates whether there is storage available for new SMS messages.
+ * @param available true if storage is available
+ * @param result callback message
+ */
+ void reportSmsMemoryStatus(boolean available, Message result);
+
+ /**
+ * Indicates to the vendor ril that StkService is running
+ * and is ready to receive RIL_UNSOL_STK_XXXX commands.
+ *
+ * @param result callback message
+ */
+ void reportStkServiceIsRunning(Message result);
+
+ void invokeOemRilRequestRaw(byte[] data, Message response);
+
+ void invokeOemRilRequestStrings(String[] strings, Message response);
+
+
+ /**
+ * Send TERMINAL RESPONSE to the SIM, after processing a proactive command
+ * sent by the SIM.
+ *
+ * @param contents String containing SAT/USAT response in hexadecimal
+ * format starting with first byte of response data. See
+ * TS 102 223 for details.
+ * @param response Callback message
+ */
+ public void sendTerminalResponse(String contents, Message response);
+
+ /**
+ * Send ENVELOPE to the SIM, after processing a proactive command sent by
+ * the SIM.
+ *
+ * @param contents String containing SAT/USAT response in hexadecimal
+ * format starting with command tag. See TS 102 223 for
+ * details.
+ * @param response Callback message
+ */
+ public void sendEnvelope(String contents, Message response);
+
+ /**
+ * Send ENVELOPE to the SIM, such as an SMS-PP data download envelope
+ * for a SIM data download message. This method has one difference
+ * from {@link #sendEnvelope}: The SW1 and SW2 status bytes from the UICC response
+ * are returned along with the response data.
+ *
+ * response.obj will be an AsyncResult
+ * response.obj.result will be an IccIoResult on success
+ *
+ * @param contents String containing SAT/USAT response in hexadecimal
+ * format starting with command tag. See TS 102 223 for
+ * details.
+ * @param response Callback message
+ */
+ public void sendEnvelopeWithStatus(String contents, Message response);
+
+ /**
+ * Accept or reject the call setup request from SIM.
+ *
+ * @param accept true if the call is to be accepted, false otherwise.
+ * @param response Callback message
+ */
+ public void handleCallSetupRequestFromSim(boolean accept, Message response);
+
+ /**
+ * Activate or deactivate cell broadcast SMS for GSM.
+ *
+ * @param activate
+ * true = activate, false = deactivate
+ * @param result Callback message is empty on completion
+ */
+ public void setGsmBroadcastActivation(boolean activate, Message result);
+
+ /**
+ * Configure cell broadcast SMS for GSM.
+ *
+ * @param response Callback message is empty on completion
+ */
+ public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response);
+
+ /**
+ * Query the current configuration of cell broadcast SMS of GSM.
+ *
+ * @param response
+ * Callback message contains the configuration from the modem
+ * on completion
+ */
+ public void getGsmBroadcastConfig(Message response);
+
+ //***** new Methods for CDMA support
+
+ /**
+ * Request the device ESN / MEID / IMEI / IMEISV.
+ * "response" is const char **
+ * [0] is IMEI if GSM subscription is available
+ * [1] is IMEISV if GSM subscription is available
+ * [2] is ESN if CDMA subscription is available
+ * [3] is MEID if CDMA subscription is available
+ */
+ public void getDeviceIdentity(Message response);
+
+ /**
+ * Request the device MDN / H_SID / H_NID / MIN.
+ * "response" is const char **
+ * [0] is MDN if CDMA subscription is available
+ * [1] is a comma separated list of H_SID (Home SID) in decimal format
+ * if CDMA subscription is available
+ * [2] is a comma separated list of H_NID (Home NID) in decimal format
+ * if CDMA subscription is available
+ * [3] is MIN (10 digits, MIN2+MIN1) if CDMA subscription is available
+ */
+ public void getCDMASubscription(Message response);
+
+ /**
+ * Send Flash Code.
+ * "response" is is NULL
+ * [0] is a FLASH string
+ */
+ public void sendCDMAFeatureCode(String FeatureCode, Message response);
+
+ /** Set the Phone type created */
+ void setPhoneType(int phoneType);
+
+ /**
+ * Query the CDMA roaming preference setting
+ *
+ * @param response is callback message to report one of CDMA_RM_*
+ */
+ void queryCdmaRoamingPreference(Message response);
+
+ /**
+ * Requests to set the CDMA roaming preference
+ * @param cdmaRoamingType one of CDMA_RM_*
+ * @param response is callback message
+ */
+ void setCdmaRoamingPreference(int cdmaRoamingType, Message response);
+
+ /**
+ * Requests to set the CDMA subscription mode
+ * @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_*
+ * @param response is callback message
+ */
+ void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response);
+
+ /**
+ * Requests to get the CDMA subscription srouce
+ * @param response is callback message
+ */
+ void getCdmaSubscriptionSource(Message response);
+
+ /**
+ * Set the TTY mode
+ *
+ * @param ttyMode one of the following:
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ * @param response is callback message
+ */
+ void setTTYMode(int ttyMode, Message response);
+
+ /**
+ * Query the TTY mode
+ * (AsyncResult)response.obj).result is an int[] with element [0] set to
+ * tty mode:
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ * @param response is callback message
+ */
+ void queryTTYMode(Message response);
+
+ /**
+ * Setup a packet data connection On successful completion, the result
+ * message will return a {@link DataCallState} object containing the connection
+ * information.
+ *
+ * @param radioTechnology
+ * indicates whether to setup connection on radio technology CDMA
+ * (0) or GSM/UMTS (1)
+ * @param profile
+ * Profile Number or NULL to indicate default profile
+ * @param apn
+ * the APN to connect to if radio technology is GSM/UMTS.
+ * Otherwise null for CDMA.
+ * @param user
+ * the username for APN, or NULL
+ * @param password
+ * the password for APN, or NULL
+ * @param authType
+ * the PAP / CHAP auth type. Values is one of SETUP_DATA_AUTH_*
+ * @param protocol
+ * one of the PDP_type values in TS 27.007 section 10.1.1.
+ * For example, "IP", "IPV6", "IPV4V6", or "PPP".
+ * @param result
+ * Callback message
+ */
+ public void setupDataCall(String radioTechnology, String profile,
+ String apn, String user, String password, String authType,
+ String protocol, Message result);
+
+ /**
+ * Deactivate packet data connection
+ *
+ * @param cid
+ * The connection ID
+ * @param reason
+ * Data disconnect reason.
+ * @param result
+ * Callback message is empty on completion
+ */
+ public void deactivateDataCall(int cid, int reason, Message result);
+
+ /**
+ * Activate or deactivate cell broadcast SMS for CDMA.
+ *
+ * @param activate
+ * true = activate, false = deactivate
+ * @param result
+ * Callback message is empty on completion
+ */
+ public void setCdmaBroadcastActivation(boolean activate, Message result);
+
+ /**
+ * Configure cdma cell broadcast SMS.
+ *
+ * @param result
+ * Callback message is empty on completion
+ */
+ // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig
+ public void setCdmaBroadcastConfig(int[] configValuesArray, Message result);
+
+ /**
+ * Query the current configuration of cdma cell broadcast SMS.
+ *
+ * @param result
+ * Callback message contains the configuration from the modem on completion
+ */
+ public void getCdmaBroadcastConfig(Message result);
+
+ /**
+ * Requests the radio's system selection module to exit emergency callback mode.
+ * This function should only be called from CDMAPHone.java.
+ *
+ * @param response callback message
+ */
+ public void exitEmergencyCallbackMode(Message response);
+
+ /**
+ * Request the status of the ICC and UICC cards.
+ *
+ * @param result
+ * Callback message containing {@link IccCardStatus} structure for the card.
+ */
+ public void getIccCardStatus(Message result);
+
+ /**
+ * Return if the current radio is LTE on CDMA. This
+ * is a tri-state return value as for a period of time
+ * the mode may be unknown.
+ *
+ * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
+ * or {@link Phone#LTE_ON_CDMA_TRUE}
+ */
+ public int getLteOnCdmaMode();
+
+ /**
+ * Request the ISIM application on the UICC to perform the AKA
+ * challenge/response algorithm for IMS authentication. The nonce string
+ * and challenge response are Base64 encoded Strings.
+ *
+ * @param nonce the nonce string to pass with the ISIM authentication request
+ * @param response a callback message with the String response in the obj field
+ */
+ public void requestIsimAuthentication(String nonce, Message response);
+
+ /**
+ * Notifiy that we are testing an emergency call
+ */
+ public void testingEmergencyCall();
+}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
new file mode 100644
index 0000000000000000000000000000000000000000..554d974b802c9b09d6a1bf7fdbc4577b14471011
--- /dev/null
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -0,0 +1,309 @@
+/*
+ * 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.util.Log;
+
+/**
+ * {@hide}
+ */
+public abstract class Connection {
+
+ //Caller Name Display
+ protected String cnapName;
+ protected int cnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
+
+ private static String LOG_TAG = "TelephonyConnection";
+
+ public enum DisconnectCause {
+ NOT_DISCONNECTED, /* has not yet disconnected */
+ INCOMING_MISSED, /* an incoming call that was missed and never answered */
+ NORMAL, /* normal; remote */
+ LOCAL, /* normal; local hangup */
+ BUSY, /* outgoing call to busy line */
+ CONGESTION, /* outgoing call to congested network */
+ MMI, /* not presently used; dial() returns null */
+ INVALID_NUMBER, /* invalid dial string */
+ NUMBER_UNREACHABLE, /* cannot reach the peer */
+ SERVER_UNREACHABLE, /* cannot reach the server */
+ INVALID_CREDENTIALS, /* invalid credentials */
+ OUT_OF_NETWORK, /* calling from out of network is not allowed */
+ SERVER_ERROR, /* server error */
+ TIMED_OUT, /* client timed out */
+ LOST_SIGNAL,
+ LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */
+ INCOMING_REJECTED, /* an incoming call that was rejected */
+ POWER_OFF, /* radio is turned off explicitly */
+ OUT_OF_SERVICE, /* out of service */
+ ICC_ERROR, /* No ICC, ICC locked, or other ICC error */
+ CALL_BARRED, /* call was blocked by call barring */
+ FDN_BLOCKED, /* call was blocked by fixed dial number */
+ CS_RESTRICTED, /* call was blocked by restricted all voice access */
+ CS_RESTRICTED_NORMAL, /* call was blocked by restricted normal voice access */
+ CS_RESTRICTED_EMERGENCY, /* call was blocked by restricted emergency voice access */
+ UNOBTAINABLE_NUMBER, /* Unassigned number (3GPP TS 24.008 table 10.5.123) */
+ CDMA_LOCKED_UNTIL_POWER_CYCLE, /* MS is locked until next power cycle */
+ CDMA_DROP,
+ CDMA_INTERCEPT, /* INTERCEPT order received, MS state idle entered */
+ CDMA_REORDER, /* MS has been redirected, call is cancelled */
+ CDMA_SO_REJECT, /* service option rejection */
+ CDMA_RETRY_ORDER, /* requested service is rejected, retry delay is set */
+ CDMA_ACCESS_FAILURE,
+ CDMA_PREEMPTED,
+ CDMA_NOT_EMERGENCY, /* not an emergency call */
+ CDMA_ACCESS_BLOCKED, /* Access Blocked by CDMA network */
+ ERROR_UNSPECIFIED
+ }
+
+ Object userData;
+
+ /* Instance Methods */
+
+ /**
+ * Gets address (e.g. phone number) associated with connection.
+ * TODO: distinguish reasons for unavailability
+ *
+ * @return address or null if unavailable
+ */
+
+ public abstract String getAddress();
+
+ /**
+ * Gets CNAP name associated with connection.
+ * @return cnap name or null if unavailable
+ */
+ public String getCnapName() {
+ return cnapName;
+ }
+
+ /**
+ * Get original dial string.
+ * @return original dial string or null if unavailable
+ */
+ public String getOrigDialString(){
+ return null;
+ }
+
+ /**
+ * Gets CNAP presentation associated with connection.
+ * @return cnap name or null if unavailable
+ */
+
+ public int getCnapNamePresentation() {
+ return cnapNamePresentation;
+ };
+
+ /**
+ * @return Call that owns this Connection, or null if none
+ */
+ public abstract Call getCall();
+
+ /**
+ * Connection create time in currentTimeMillis() format
+ * Basically, set when object is created.
+ * Effectively, when an incoming call starts ringing or an
+ * outgoing call starts dialing
+ */
+ public abstract long getCreateTime();
+
+ /**
+ * Connection connect time in currentTimeMillis() format.
+ * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
+ * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
+ * Returns 0 before then.
+ */
+ public abstract long getConnectTime();
+
+ /**
+ * Disconnect time in currentTimeMillis() format.
+ * The time when this Connection makes a transition into ENDED or FAIL.
+ * Returns 0 before then.
+ */
+ public abstract long getDisconnectTime();
+
+ /**
+ * Returns the number of milliseconds the call has been connected,
+ * or 0 if the call has never connected.
+ * If the call is still connected, then returns the elapsed
+ * time since connect.
+ */
+ public abstract long getDurationMillis();
+
+ /**
+ * If this connection is HOLDING, return the number of milliseconds
+ * that it has been on hold for (approximately).
+ * If this connection is in any other state, return 0.
+ */
+
+ public abstract long getHoldDurationMillis();
+
+ /**
+ * Returns "NOT_DISCONNECTED" if not yet disconnected.
+ */
+ public abstract DisconnectCause getDisconnectCause();
+
+ /**
+ * 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).
+ */
+ public abstract boolean isIncoming();
+
+ /**
+ * If this Connection is connected, then it is associated with
+ * a Call.
+ *
+ * Returns getCall().getState() or Call.State.IDLE if not
+ * connected
+ */
+ public Call.State getState() {
+ Call c;
+
+ c = getCall();
+
+ if (c == null) {
+ return Call.State.IDLE;
+ } else {
+ return c.getState();
+ }
+ }
+
+ /**
+ * isAlive()
+ *
+ * @return true if the connection isn't disconnected
+ * (could be active, holding, ringing, dialing, etc)
+ */
+ public boolean
+ isAlive() {
+ return getState().isAlive();
+ }
+
+ /**
+ * Returns true if Connection is connected and is INCOMING or WAITING
+ */
+ public boolean
+ isRinging() {
+ return getState().isRinging();
+ }
+
+ /**
+ *
+ * @return the userdata set in setUserData()
+ */
+ public Object getUserData() {
+ return userData;
+ }
+
+ /**
+ *
+ * @param userdata user can store an any userdata in the Connection object.
+ */
+ public void setUserData(Object userdata) {
+ this.userData = userdata;
+ }
+
+ /**
+ * Hangup individual Connection
+ */
+ public abstract void hangup() throws CallStateException;
+
+ /**
+ * Separate this call from its owner Call and assigns it to a new Call
+ * (eg if it is currently part of a Conference call
+ * TODO: Throw exception? Does GSM require error display on failure here?
+ */
+ public abstract void separate() throws CallStateException;
+
+ public enum PostDialState {
+ NOT_STARTED, /* The post dial string playback hasn't
+ been started, or this call is not yet
+ connected, or this is an incoming call */
+ STARTED, /* The post dial string playback has begun */
+ WAIT, /* The post dial string playback is waiting for a
+ call to proceedAfterWaitChar() */
+ WILD, /* The post dial string playback is waiting for a
+ call to proceedAfterWildChar() */
+ COMPLETE, /* The post dial string playback is complete */
+ CANCELLED, /* The post dial string playback was cancelled
+ with cancelPostDial() */
+ PAUSE /* The post dial string playback is pausing for a
+ call to processNextPostDialChar*/
+ }
+
+ public void clearUserData(){
+ userData = null;
+ }
+
+ public abstract PostDialState getPostDialState();
+
+ /**
+ * Returns the portion of the post dial string that has not
+ * yet been dialed, or "" if none
+ */
+ public abstract String getRemainingPostDialString();
+
+ /**
+ * See Phone.setOnPostDialWaitCharacter()
+ */
+
+ public abstract void proceedAfterWaitChar();
+
+ /**
+ * See Phone.setOnPostDialWildCharacter()
+ */
+ public abstract void proceedAfterWildChar(String str);
+ /**
+ * Cancel any post
+ */
+ public abstract void cancelPostDial();
+
+ /**
+ * Returns the caller id presentation type for incoming and waiting calls
+ * @return one of PRESENTATION_*
+ */
+ public abstract int getNumberPresentation();
+
+ /**
+ * Returns the User to User Signaling (UUS) information associated with
+ * incoming and waiting calls
+ * @return UUSInfo containing the UUS userdata.
+ */
+ public abstract UUSInfo getUUSInfo();
+
+ /**
+ * Build a human representation of a connection instance, suitable for debugging.
+ * Don't log personal stuff unless in debug mode.
+ * @return a string representing the internal state of this connection.
+ */
+ public String toString() {
+ StringBuilder str = new StringBuilder(128);
+
+ if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+ str.append("addr: " + getAddress())
+ .append(" pres.: " + getNumberPresentation())
+ .append(" dial: " + getOrigDialString())
+ .append(" postdial: " + getRemainingPostDialString())
+ .append(" cnap name: " + getCnapName())
+ .append("(" + getCnapNamePresentation() + ")");
+ }
+ str.append(" incoming: " + isIncoming())
+ .append(" state: " + getState())
+ .append(" post dial state: " + getPostDialState());
+ return str.toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DataCallState.java b/src/java/com/android/internal/telephony/DataCallState.java
new file mode 100644
index 0000000000000000000000000000000000000000..efbf608d90205c40ce6011ad830e09160c48dc5c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DataCallState.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2009 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.telephony.DataConnection.FailCause;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This is RIL_Data_Call_Response_v5 from ril.h
+ * TODO: Rename to DataCallResponse.
+ */
+public class DataCallState {
+ private final boolean DBG = true;
+ private final String LOG_TAG = "GSM";
+
+ public int version = 0;
+ public int status = 0;
+ public int cid = 0;
+ public int active = 0;
+ public String type = "";
+ public String ifname = "";
+ public String [] addresses = new String[0];
+ public String [] dnses = new String[0];
+ public String[] gateways = new String[0];
+ public int suggestedRetryTime = -1;
+
+ /**
+ * Class returned by onSetupConnectionCompleted.
+ */
+ public enum SetupResult {
+ SUCCESS,
+ ERR_BadCommand,
+ ERR_UnacceptableParameter,
+ ERR_GetLastErrorFromRil,
+ ERR_Stale,
+ ERR_RilError;
+
+ public FailCause mFailCause;
+
+ SetupResult() {
+ mFailCause = FailCause.fromInt(0);
+ }
+
+ @Override
+ public String toString() {
+ return name() + " SetupResult.mFailCause=" + mFailCause;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("DataCallState: {")
+ .append("version=").append(version)
+ .append(" status=").append(status)
+ .append(" retry=").append(suggestedRetryTime)
+ .append(" cid=").append(cid)
+ .append(" active=").append(active)
+ .append(" type=").append(type)
+ .append("' ifname='").append(ifname);
+ sb.append("' addresses=[");
+ for (String addr : addresses) {
+ sb.append(addr);
+ sb.append(",");
+ }
+ if (addresses.length > 0) sb.deleteCharAt(sb.length()-1);
+ sb.append("] dnses=[");
+ for (String addr : dnses) {
+ sb.append(addr);
+ sb.append(",");
+ }
+ if (dnses.length > 0) sb.deleteCharAt(sb.length()-1);
+ sb.append("] gateways=[");
+ for (String addr : gateways) {
+ sb.append(addr);
+ sb.append(",");
+ }
+ if (gateways.length > 0) sb.deleteCharAt(sb.length()-1);
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ public SetupResult setLinkProperties(LinkProperties linkProperties,
+ boolean okToUseSystemPropertyDns) {
+ SetupResult result;
+
+ // Start with clean network properties and if we have
+ // a failure we'll clear again at the bottom of this code.
+ if (linkProperties == null)
+ linkProperties = new LinkProperties();
+ else
+ linkProperties.clear();
+
+ if (status == FailCause.NONE.getErrorCode()) {
+ String propertyPrefix = "net." + ifname + ".";
+
+ try {
+ // set interface name
+ linkProperties.setInterfaceName(ifname);
+
+ // set link addresses
+ if (addresses != null && addresses.length > 0) {
+ for (String addr : addresses) {
+ addr = addr.trim();
+ if (addr.isEmpty()) continue;
+ LinkAddress la;
+ int addrPrefixLen;
+
+ String [] ap = addr.split("/");
+ if (ap.length == 2) {
+ addr = ap[0];
+ addrPrefixLen = Integer.parseInt(ap[1]);
+ } else {
+ addrPrefixLen = 0;
+ }
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(addr);
+ } catch (IllegalArgumentException e) {
+ throw new UnknownHostException("Non-numeric ip addr=" + addr);
+ }
+ if (! ia.isAnyLocalAddress()) {
+ if (addrPrefixLen == 0) {
+ // Assume point to point
+ addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
+ }
+ if (DBG) Log.d(LOG_TAG, "addr/pl=" + addr + "/" + addrPrefixLen);
+ la = new LinkAddress(ia, addrPrefixLen);
+ linkProperties.addLinkAddress(la);
+ }
+ }
+ } else {
+ throw new UnknownHostException("no address for ifname=" + ifname);
+ }
+
+ // set dns servers
+ if (dnses != null && dnses.length > 0) {
+ for (String addr : dnses) {
+ addr = addr.trim();
+ if (addr.isEmpty()) continue;
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(addr);
+ } catch (IllegalArgumentException e) {
+ throw new UnknownHostException("Non-numeric dns addr=" + addr);
+ }
+ if (! ia.isAnyLocalAddress()) {
+ linkProperties.addDns(ia);
+ }
+ }
+ } else if (okToUseSystemPropertyDns){
+ String dnsServers[] = new String[2];
+ dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+ dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+ for (String dnsAddr : dnsServers) {
+ dnsAddr = dnsAddr.trim();
+ if (dnsAddr.isEmpty()) continue;
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(dnsAddr);
+ } catch (IllegalArgumentException e) {
+ throw new UnknownHostException("Non-numeric dns addr=" + dnsAddr);
+ }
+ if (! ia.isAnyLocalAddress()) {
+ linkProperties.addDns(ia);
+ }
+ }
+ } else {
+ throw new UnknownHostException("Empty dns response and no system default dns");
+ }
+
+ // set gateways
+ if ((gateways == null) || (gateways.length == 0)) {
+ String sysGateways = SystemProperties.get(propertyPrefix + "gw");
+ if (sysGateways != null) {
+ gateways = sysGateways.split(" ");
+ } else {
+ gateways = new String[0];
+ }
+ }
+ for (String addr : gateways) {
+ addr = addr.trim();
+ if (addr.isEmpty()) continue;
+ InetAddress ia;
+ try {
+ ia = NetworkUtils.numericToInetAddress(addr);
+ } catch (IllegalArgumentException e) {
+ throw new UnknownHostException("Non-numeric gateway addr=" + addr);
+ }
+ if (! ia.isAnyLocalAddress()) {
+ linkProperties.addRoute(new RouteInfo(ia));
+ }
+ }
+
+ result = SetupResult.SUCCESS;
+ } catch (UnknownHostException e) {
+ Log.d(LOG_TAG, "setLinkProperties: UnknownHostException " + e);
+ e.printStackTrace();
+ result = SetupResult.ERR_UnacceptableParameter;
+ }
+ } else {
+ if (version < 4) {
+ result = SetupResult.ERR_GetLastErrorFromRil;
+ } else {
+ result = SetupResult.ERR_RilError;
+ }
+ }
+
+ // An error occurred so clear properties
+ if (result != SetupResult.SUCCESS) {
+ if(DBG) {
+ Log.d(LOG_TAG, "setLinkProperties: error clearing LinkProperties " +
+ "status=" + status + " result=" + result);
+ }
+ linkProperties.clear();
+ }
+
+ return result;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DataConnection.java b/src/java/com/android/internal/telephony/DataConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..9751040695197b710aed4b2ad8632190248b128a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DataConnection.java
@@ -0,0 +1,1286 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+
+import com.android.internal.telephony.DataCallState.SetupResult;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import android.app.PendingIntent;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.ProxyProperties;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.TimeUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * {@hide}
+ *
+ * DataConnection StateMachine.
+ *
+ * This is an abstract base class for representing a single data connection.
+ * Instances of this class such as CdmaDataConnection
and
+ * GsmDataConnection
, * represent a connection via the cellular network.
+ * There may be multiple data connections and all of them are managed by the
+ * DataConnectionTracker
.
+ *
+ * Instances are asynchronous state machines and have two primary entry points
+ * connect()
and disconnect
. The message a parameter will be returned
+ * hen the operation completes. The msg.obj
will contain an AsyncResult
+ * object and AsyncResult.userObj
is the original msg.obj
. if successful
+ * with the AsyncResult.result == null
and AsyncResult.exception == null
.
+ * If an error AsyncResult.result = FailCause
and
+ * AsyncResult.exception = new Exception()
.
+ *
+ * The other public methods are provided for debugging.
+ */
+public abstract class DataConnection extends StateMachine {
+ protected static final boolean DBG = true;
+ protected static final boolean VDBG = false;
+
+ protected static AtomicInteger mCount = new AtomicInteger(0);
+ protected AsyncChannel mAc;
+
+ protected List mApnList = null;
+ PendingIntent mReconnectIntent = null;
+
+ private DataConnectionTracker mDataConnectionTracker = null;
+
+ /**
+ * Used internally for saving connecting parameters.
+ */
+ protected static class ConnectionParams {
+ public ConnectionParams(ApnSetting apn, Message onCompletedMsg) {
+ this.apn = apn;
+ this.onCompletedMsg = onCompletedMsg;
+ }
+
+ public int tag;
+ public ApnSetting apn;
+ public Message onCompletedMsg;
+ }
+
+ /**
+ * Used internally for saving disconnecting parameters.
+ */
+ protected static class DisconnectParams {
+ public DisconnectParams(String reason, Message onCompletedMsg) {
+ this.reason = reason;
+ this.onCompletedMsg = onCompletedMsg;
+ }
+ public int tag;
+ public String reason;
+ public Message onCompletedMsg;
+ }
+
+ /**
+ * Returned as the reason for a connection failure as defined
+ * by RIL_DataCallFailCause in ril.h and some local errors.
+ */
+ public enum FailCause {
+ NONE(0),
+
+ // This series of errors as specified by the standards
+ // specified in ril.h
+ OPERATOR_BARRED(0x08),
+ INSUFFICIENT_RESOURCES(0x1A),
+ MISSING_UNKNOWN_APN(0x1B),
+ UNKNOWN_PDP_ADDRESS_TYPE(0x1C),
+ USER_AUTHENTICATION(0x1D),
+ ACTIVATION_REJECT_GGSN(0x1E),
+ ACTIVATION_REJECT_UNSPECIFIED(0x1F),
+ SERVICE_OPTION_NOT_SUPPORTED(0x20),
+ SERVICE_OPTION_NOT_SUBSCRIBED(0x21),
+ SERVICE_OPTION_OUT_OF_ORDER(0x22),
+ NSAPI_IN_USE(0x23),
+ ONLY_IPV4_ALLOWED(0x32),
+ ONLY_IPV6_ALLOWED(0x33),
+ ONLY_SINGLE_BEARER_ALLOWED(0x34),
+ PROTOCOL_ERRORS(0x6F),
+
+ // Local errors generated by Vendor RIL
+ // specified in ril.h
+ REGISTRATION_FAIL(-1),
+ GPRS_REGISTRATION_FAIL(-2),
+ SIGNAL_LOST(-3),
+ PREF_RADIO_TECH_CHANGED(-4),
+ RADIO_POWER_OFF(-5),
+ TETHERED_CALL_ACTIVE(-6),
+ ERROR_UNSPECIFIED(0xFFFF),
+
+ // Errors generated by the Framework
+ // specified here
+ UNKNOWN(0x10000),
+ RADIO_NOT_AVAILABLE(0x10001),
+ UNACCEPTABLE_NETWORK_PARAMETER(0x10002),
+ CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003);
+
+ private final int mErrorCode;
+ private static final HashMap sErrorCodeToFailCauseMap;
+ static {
+ sErrorCodeToFailCauseMap = new HashMap();
+ for (FailCause fc : values()) {
+ sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
+ }
+ }
+
+ FailCause(int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ int getErrorCode() {
+ return mErrorCode;
+ }
+
+ public boolean isPermanentFail() {
+ return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
+ (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
+ (this == SERVICE_OPTION_NOT_SUPPORTED) ||
+ (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
+ (this == PROTOCOL_ERRORS);
+ }
+
+ public boolean isEventLoggable() {
+ return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
+ (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
+ (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
+ (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
+ (this == SERVICE_OPTION_NOT_SUPPORTED) ||
+ (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
+ (this == PROTOCOL_ERRORS) ||
+ (this == UNACCEPTABLE_NETWORK_PARAMETER);
+ }
+
+ public static FailCause fromInt(int errorCode) {
+ FailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
+ if (fc == null) {
+ fc = UNKNOWN;
+ }
+ return fc;
+ }
+ }
+
+ public static class CallSetupException extends Exception {
+ private int mRetryOverride = -1;
+
+ CallSetupException (int retryOverride) {
+ mRetryOverride = retryOverride;
+ }
+
+ public int getRetryOverride() {
+ return mRetryOverride;
+ }
+ }
+
+ // ***** Event codes for driving the state machine
+ protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
+ protected static final int EVENT_CONNECT = BASE + 0;
+ protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
+ protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
+ protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
+ protected static final int EVENT_DISCONNECT = BASE + 4;
+ protected static final int EVENT_RIL_CONNECTED = BASE + 5;
+ protected static final int EVENT_DISCONNECT_ALL = BASE + 6;
+
+ private static final int CMD_TO_STRING_COUNT = EVENT_DISCONNECT_ALL - BASE + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
+ sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
+ "EVENT_SETUP_DATA_CONNECTION_DONE";
+ sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
+ sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
+ sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
+ sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
+ sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
+ }
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return null;
+ }
+ }
+
+ //***** Tag IDs for EventLog
+ protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
+
+ //***** Member Variables
+ protected ApnSetting mApn;
+ protected int mTag;
+ protected PhoneBase phone;
+ protected int mRilVersion = -1;
+ protected int cid;
+ protected LinkProperties mLinkProperties = new LinkProperties();
+ protected LinkCapabilities mCapabilities = new LinkCapabilities();
+ protected long createTime;
+ protected long lastFailTime;
+ protected FailCause lastFailCause;
+ protected int mRetryOverride = -1;
+ protected static final String NULL_IP = "0.0.0.0";
+ protected int mRefCount;
+ Object userData;
+
+ //***** Abstract methods
+ @Override
+ public abstract String toString();
+
+ protected abstract void onConnect(ConnectionParams cp);
+
+ protected abstract boolean isDnsOk(String[] domainNameServers);
+
+ protected abstract void log(String s);
+
+ //***** Constructor
+ protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
+ DataConnectionTracker dct) {
+ super(name);
+ setLogRecSize(100);
+ if (DBG) log("DataConnection constructor E");
+ this.phone = phone;
+ this.mDataConnectionTracker = dct;
+ mId = id;
+ mRetryMgr = rm;
+ this.cid = -1;
+
+ setDbg(false);
+ addState(mDefaultState);
+ addState(mInactiveState, mDefaultState);
+ addState(mActivatingState, mDefaultState);
+ addState(mActiveState, mDefaultState);
+ addState(mDisconnectingState, mDefaultState);
+ addState(mDisconnectingErrorCreatingConnection, mDefaultState);
+ setInitialState(mInactiveState);
+
+ mApnList = new ArrayList();
+ if (DBG) log("DataConnection constructor X");
+ }
+
+ /**
+ * Shut down this instance and its state machine.
+ */
+ private void shutDown() {
+ if (DBG) log("shutDown");
+
+ if (mAc != null) {
+ mAc.disconnected();
+ mAc = null;
+ }
+ mApnList = null;
+ mReconnectIntent = null;
+ mDataConnectionTracker = null;
+ mApn = null;
+ phone = null;
+ mLinkProperties = null;
+ mCapabilities = null;
+ lastFailCause = null;
+ userData = null;
+ }
+
+ /**
+ * TearDown the data connection.
+ *
+ * @param o will be returned in AsyncResult.userObj
+ * and is either a DisconnectParams or ConnectionParams.
+ */
+ private void tearDownData(Object o) {
+ int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+ if ((o != null) && (o instanceof DisconnectParams)) {
+ DisconnectParams dp = (DisconnectParams)o;
+ Message m = dp.onCompletedMsg;
+ if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
+ discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+ } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
+ discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
+ }
+ }
+ if (phone.mCM.getRadioState().isOn()) {
+ if (DBG) log("tearDownData radio is on, call deactivateDataCall");
+ phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o));
+ } else {
+ if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
+ AsyncResult ar = new AsyncResult(o, null, null);
+ sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar));
+ }
+ }
+
+ /**
+ * Send the connectionCompletedMsg.
+ *
+ * @param cp is the ConnectionParams
+ * @param cause
+ */
+ private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) {
+ Message connectionCompletedMsg = cp.onCompletedMsg;
+ if (connectionCompletedMsg == null) {
+ return;
+ }
+
+ long timeStamp = System.currentTimeMillis();
+ connectionCompletedMsg.arg1 = cid;
+
+ if (cause == FailCause.NONE) {
+ createTime = timeStamp;
+ AsyncResult.forMessage(connectionCompletedMsg);
+ } else {
+ lastFailCause = cause;
+ lastFailTime = timeStamp;
+ AsyncResult.forMessage(connectionCompletedMsg, cause,
+ new CallSetupException(mRetryOverride));
+ }
+ if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause);
+
+ connectionCompletedMsg.sendToTarget();
+ }
+
+ /**
+ * Send ar.userObj if its a message, which is should be back to originator.
+ *
+ * @param dp is the DisconnectParams.
+ */
+ private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
+ if (VDBG) log("NotifyDisconnectCompleted");
+
+ ApnContext alreadySent = null;
+ String reason = null;
+
+ if (dp.onCompletedMsg != null) {
+ // Get ApnContext, but only valid on GSM devices this is a string on CDMA devices.
+ Message msg = dp.onCompletedMsg;
+ if (msg.obj instanceof ApnContext) {
+ alreadySent = (ApnContext)msg.obj;
+ }
+ reason = dp.reason;
+ if (VDBG) {
+ log(String.format("msg=%s msg.obj=%s", msg.toString(),
+ ((msg.obj instanceof String) ? (String) msg.obj : "")));
+ }
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ }
+ if (sendAll) {
+ for (ApnContext a : mApnList) {
+ if (a == alreadySent) continue;
+ if (reason != null) a.setReason(reason);
+ Message msg = mDataConnectionTracker.obtainMessage(
+ DctConstants.EVENT_DISCONNECT_DONE, a);
+ AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ }
+ }
+
+ if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
+ }
+
+ protected int getRilRadioTechnology(int defaultRilRadioTechnology) {
+ int rilRadioTechnology;
+ if (mRilVersion < 6) {
+ rilRadioTechnology = defaultRilRadioTechnology;
+ } else {
+ rilRadioTechnology = phone.getServiceState().getRilRadioTechnology() + 2;
+ }
+ return rilRadioTechnology;
+ }
+
+ /*
+ * **************************************************************************
+ * Begin Members and methods owned by DataConnectionTracker but stored
+ * in a DataConnection because there is one per connection.
+ * **************************************************************************
+ */
+
+ /*
+ * The id is owned by DataConnectionTracker.
+ */
+ private int mId;
+
+ /**
+ * Get the DataConnection ID
+ */
+ public int getDataConnectionId() {
+ return mId;
+ }
+
+ /*
+ * The retry manager is currently owned by the DataConnectionTracker but is stored
+ * in the DataConnection because there is one per connection. These methods
+ * should only be used by the DataConnectionTracker although someday the retrying
+ * maybe managed by the DataConnection itself and these methods could disappear.
+ */
+ private RetryManager mRetryMgr;
+
+ /**
+ * @return retry manager retryCount
+ */
+ public int getRetryCount() {
+ return mRetryMgr.getRetryCount();
+ }
+
+ /**
+ * set retry manager retryCount
+ */
+ public void setRetryCount(int retryCount) {
+ if (DBG) log("setRetryCount: " + retryCount);
+ mRetryMgr.setRetryCount(retryCount);
+ }
+
+ /**
+ * @return retry manager retryTimer
+ */
+ public int getRetryTimer() {
+ return mRetryMgr.getRetryTimer();
+ }
+
+ /**
+ * increaseRetryCount of retry manager
+ */
+ public void increaseRetryCount() {
+ mRetryMgr.increaseRetryCount();
+ }
+
+ /**
+ * @return retry manager isRetryNeeded
+ */
+ public boolean isRetryNeeded() {
+ return mRetryMgr.isRetryNeeded();
+ }
+
+ /**
+ * resetRetryCount of retry manager
+ */
+ public void resetRetryCount() {
+ mRetryMgr.resetRetryCount();
+ }
+
+ /**
+ * set retryForeverUsingLasttimeout of retry manager
+ */
+ public void retryForeverUsingLastTimeout() {
+ mRetryMgr.retryForeverUsingLastTimeout();
+ }
+
+ /**
+ * @return retry manager isRetryForever
+ */
+ public boolean isRetryForever() {
+ return mRetryMgr.isRetryForever();
+ }
+
+ /**
+ * @return whether the retry config is set successfully or not
+ */
+ public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
+ return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
+ }
+
+ /**
+ * @return whether the retry config is set successfully or not
+ */
+ public boolean configureRetry(String configStr) {
+ return mRetryMgr.configure(configStr);
+ }
+
+ /*
+ * **************************************************************************
+ * End members owned by DataConnectionTracker
+ * **************************************************************************
+ */
+
+ /**
+ * Clear all settings called when entering mInactiveState.
+ */
+ protected void clearSettings() {
+ if (DBG) log("clearSettings");
+
+ createTime = -1;
+ lastFailTime = -1;
+ lastFailCause = FailCause.NONE;
+ mRetryOverride = -1;
+ mRefCount = 0;
+ cid = -1;
+
+ mLinkProperties = new LinkProperties();
+ mApn = null;
+ }
+
+ /**
+ * Process setup completion.
+ *
+ * @param ar is the result
+ * @return SetupResult.
+ */
+ private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
+ DataCallState response = (DataCallState) ar.result;
+ ConnectionParams cp = (ConnectionParams) ar.userObj;
+ DataCallState.SetupResult result;
+
+ if (ar.exception != null) {
+ if (DBG) {
+ log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
+ " response=" + response);
+ }
+
+ if (ar.exception instanceof CommandException
+ && ((CommandException) (ar.exception)).getCommandError()
+ == CommandException.Error.RADIO_NOT_AVAILABLE) {
+ result = DataCallState.SetupResult.ERR_BadCommand;
+ result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
+ } else if ((response == null) || (response.version < 4)) {
+ result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
+ } else {
+ result = DataCallState.SetupResult.ERR_RilError;
+ result.mFailCause = FailCause.fromInt(response.status);
+ }
+ } else if (cp.tag != mTag) {
+ if (DBG) {
+ log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
+ }
+ result = DataCallState.SetupResult.ERR_Stale;
+ } else if (response.status != 0) {
+ result = DataCallState.SetupResult.ERR_RilError;
+ result.mFailCause = FailCause.fromInt(response.status);
+ } else {
+ if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
+ cid = response.cid;
+ result = updateLinkProperty(response).setupResult;
+ }
+
+ return result;
+ }
+
+ private int getSuggestedRetryTime(AsyncResult ar) {
+ int retry = -1;
+ if (ar.exception == null) {
+ DataCallState response = (DataCallState) ar.result;
+ retry = response.suggestedRetryTime;
+ }
+ return retry;
+ }
+
+ private DataCallState.SetupResult setLinkProperties(DataCallState response,
+ LinkProperties lp) {
+ // Check if system property dns usable
+ boolean okToUseSystemPropertyDns = false;
+ String propertyPrefix = "net." + response.ifname + ".";
+ String dnsServers[] = new String[2];
+ dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+ dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+ okToUseSystemPropertyDns = isDnsOk(dnsServers);
+
+ // set link properties based on data call response
+ return response.setLinkProperties(lp, okToUseSystemPropertyDns);
+ }
+
+ public static class UpdateLinkPropertyResult {
+ public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS;
+ public LinkProperties oldLp;
+ public LinkProperties newLp;
+ public UpdateLinkPropertyResult(LinkProperties curLp) {
+ oldLp = curLp;
+ newLp = curLp;
+ }
+ }
+
+ private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) {
+ UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
+
+ if (newState == null) return result;
+
+ DataCallState.SetupResult setupResult;
+ result.newLp = new LinkProperties();
+
+ // set link properties based on data call response
+ result.setupResult = setLinkProperties(newState, result.newLp);
+ if (result.setupResult != DataCallState.SetupResult.SUCCESS) {
+ if (DBG) log("updateLinkProperty failed : " + result.setupResult);
+ return result;
+ }
+ // copy HTTP proxy as it is not part DataCallState.
+ result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
+
+ if (DBG && (! result.oldLp.equals(result.newLp))) {
+ log("updateLinkProperty old LP=" + result.oldLp);
+ log("updateLinkProperty new LP=" + result.newLp);
+ }
+ mLinkProperties = result.newLp;
+
+ return result;
+ }
+
+ /**
+ * The parent state for all other states.
+ */
+ private class DcDefaultState extends State {
+ @Override
+ public void enter() {
+ phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
+ }
+ @Override
+ public void exit() {
+ phone.mCM.unregisterForRilConnected(getHandler());
+ shutDown();
+ }
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal = HANDLED;
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAc != null) {
+ if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
+ mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+ } else {
+ mAc = new AsyncChannel();
+ mAc.connected(null, getHandler(), msg.replyTo);
+ if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
+ mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
+ }
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
+ quit();
+ break;
+ }
+ case DataConnectionAc.REQ_IS_INACTIVE: {
+ boolean val = getCurrentState() == mInactiveState;
+ if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_CID: {
+ if (VDBG) log("REQ_GET_CID cid=" + cid);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_APNSETTING: {
+ if (VDBG) log("REQ_GET_APNSETTING apnSetting=" + mApn);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
+ LinkProperties lp = new LinkProperties(mLinkProperties);
+ if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
+ break;
+ }
+ case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
+ ProxyProperties proxy = (ProxyProperties) msg.obj;
+ if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
+ mLinkProperties.setHttpProxy(proxy);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
+ break;
+ }
+ case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
+ DataCallState newState = (DataCallState) msg.obj;
+ UpdateLinkPropertyResult result =
+ updateLinkProperty(newState);
+ if (VDBG) {
+ log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result="
+ + result + " newState=" + newState);
+ }
+ mAc.replyToMessage(msg,
+ DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
+ result);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
+ LinkCapabilities lc = new LinkCapabilities(mCapabilities);
+ if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
+ break;
+ }
+ case DataConnectionAc.REQ_RESET:
+ if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
+ transitionTo(mInactiveState);
+ break;
+ case DataConnectionAc.REQ_GET_REFCOUNT: {
+ if (VDBG) log("REQ_GET_REFCOUNT refCount=" + mRefCount);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
+ break;
+ }
+ case DataConnectionAc.REQ_ADD_APNCONTEXT: {
+ ApnContext apnContext = (ApnContext) msg.obj;
+ if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
+ if (!mApnList.contains(apnContext)) {
+ mApnList.add(apnContext);
+ }
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
+ break;
+ }
+ case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
+ ApnContext apnContext = (ApnContext) msg.obj;
+ if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
+ mApnList.remove(apnContext);
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
+ if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
+ new ArrayList(mApnList));
+ break;
+ }
+ case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
+ PendingIntent intent = (PendingIntent) msg.obj;
+ if (VDBG) log("REQ_SET_RECONNECT_INTENT");
+ mReconnectIntent = intent;
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
+ break;
+ }
+ case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
+ if (VDBG) log("REQ_GET_RECONNECT_INTENT");
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
+ mReconnectIntent);
+ break;
+ }
+ case EVENT_CONNECT:
+ if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
+ ConnectionParams cp = (ConnectionParams) msg.obj;
+ notifyConnectCompleted(cp, FailCause.UNKNOWN);
+ break;
+
+ case EVENT_DISCONNECT:
+ if (DBG) {
+ log("DcDefaultState deferring msg.what=EVENT_DISCONNECT" + mRefCount);
+ }
+ deferMessage(msg);
+ break;
+
+ case EVENT_DISCONNECT_ALL:
+ if (DBG) {
+ log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL" + mRefCount);
+ }
+ deferMessage(msg);
+ break;
+
+ case EVENT_RIL_CONNECTED:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ mRilVersion = (Integer)ar.result;
+ if (DBG) {
+ log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
+ mRilVersion);
+ }
+ } else {
+ log("Unexpected exception on EVENT_RIL_CONNECTED");
+ mRilVersion = -1;
+ }
+ break;
+
+ default:
+ if (DBG) {
+ log("DcDefaultState: shouldn't happen but ignore msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ break;
+ }
+
+ return retVal;
+ }
+ }
+ private DcDefaultState mDefaultState = new DcDefaultState();
+
+ /**
+ * The state machine is inactive and expects a EVENT_CONNECT.
+ */
+ private class DcInactiveState extends State {
+ private ConnectionParams mConnectionParams = null;
+ private FailCause mFailCause = null;
+ private DisconnectParams mDisconnectParams = null;
+
+ public void setEnterNotificationParams(ConnectionParams cp, FailCause cause,
+ int retryOverride) {
+ if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
+ mConnectionParams = cp;
+ mFailCause = cause;
+ mRetryOverride = retryOverride;
+ }
+
+ public void setEnterNotificationParams(DisconnectParams dp) {
+ if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
+ mDisconnectParams = dp;
+ }
+
+ @Override
+ public void enter() {
+ mTag += 1;
+
+ /**
+ * Now that we've transitioned to Inactive state we
+ * can send notifications. Previously we sent the
+ * notifications in the processMessage handler but
+ * that caused a race condition because the synchronous
+ * call to isInactive.
+ */
+ if ((mConnectionParams != null) && (mFailCause != null)) {
+ if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
+ notifyConnectCompleted(mConnectionParams, mFailCause);
+ }
+ if (mDisconnectParams != null) {
+ if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
+ notifyDisconnectCompleted(mDisconnectParams, true);
+ }
+ clearSettings();
+ }
+
+ @Override
+ public void exit() {
+ // clear notifications
+ mConnectionParams = null;
+ mFailCause = null;
+ mDisconnectParams = null;
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case DataConnectionAc.REQ_RESET:
+ if (DBG) {
+ log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
+ }
+ mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_CONNECT:
+ ConnectionParams cp = (ConnectionParams) msg.obj;
+ cp.tag = mTag;
+ if (DBG) {
+ log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
+ + mRefCount);
+ }
+ mRefCount = 1;
+ onConnect(cp);
+ transitionTo(mActivatingState);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_DISCONNECT:
+ if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
+ notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_DISCONNECT_ALL:
+ if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
+ notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
+ retVal = HANDLED;
+ break;
+
+ default:
+ if (VDBG) {
+ log("DcInactiveState nothandled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+ private DcInactiveState mInactiveState = new DcInactiveState();
+
+ /**
+ * The state machine is activating a connection.
+ */
+ private class DcActivatingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+ AsyncResult ar;
+ ConnectionParams cp;
+
+ switch (msg.what) {
+ case EVENT_CONNECT:
+ if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
+ + mRefCount);
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_SETUP_DATA_CONNECTION_DONE:
+ if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE");
+
+ ar = (AsyncResult) msg.obj;
+ cp = (ConnectionParams) ar.userObj;
+
+ DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
+ if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
+ switch (result) {
+ case SUCCESS:
+ // All is well
+ mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
+ transitionTo(mActiveState);
+ break;
+ case ERR_BadCommand:
+ // Vendor ril rejected the command and didn't connect.
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1);
+ transitionTo(mInactiveState);
+ break;
+ case ERR_UnacceptableParameter:
+ // The addresses given from the RIL are bad
+ tearDownData(cp);
+ transitionTo(mDisconnectingErrorCreatingConnection);
+ break;
+ case ERR_GetLastErrorFromRil:
+ // Request failed and this is an old RIL
+ phone.mCM.getLastDataCallFailCause(
+ obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
+ break;
+ case ERR_RilError:
+ // Request failed and mFailCause has the reason
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
+ getSuggestedRetryTime(ar));
+ transitionTo(mInactiveState);
+ break;
+ case ERR_Stale:
+ // Request is stale, ignore.
+ break;
+ default:
+ throw new RuntimeException("Unknown SetupResult, should not happen");
+ }
+ retVal = HANDLED;
+ break;
+
+ case EVENT_GET_LAST_FAIL_DONE:
+ ar = (AsyncResult) msg.obj;
+ cp = (ConnectionParams) ar.userObj;
+ FailCause cause = FailCause.UNKNOWN;
+
+ if (cp.tag == mTag) {
+ if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE");
+ if (ar.exception == null) {
+ int rilFailCause = ((int[]) (ar.result))[0];
+ cause = FailCause.fromInt(rilFailCause);
+ }
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp, cause, -1);
+ transitionTo(mInactiveState);
+ } else {
+ if (DBG) {
+ log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag="
+ + cp.tag + ", mTag=" + mTag);
+ }
+ }
+
+ retVal = HANDLED;
+ break;
+
+ default:
+ if (VDBG) {
+ log("DcActivatingState not handled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+ private DcActivatingState mActivatingState = new DcActivatingState();
+
+ /**
+ * The state machine is connected, expecting an EVENT_DISCONNECT.
+ */
+ private class DcActiveState extends State {
+ private ConnectionParams mConnectionParams = null;
+ private FailCause mFailCause = null;
+
+ public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
+ if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
+ mConnectionParams = cp;
+ mFailCause = cause;
+ }
+
+ @Override public void enter() {
+ /**
+ * Now that we've transitioned to Active state we
+ * can send notifications. Previously we sent the
+ * notifications in the processMessage handler but
+ * that caused a race condition because the synchronous
+ * call to isActive.
+ */
+ if ((mConnectionParams != null) && (mFailCause != null)) {
+ if (VDBG) log("DcActiveState: enter notifyConnectCompleted");
+ notifyConnectCompleted(mConnectionParams, mFailCause);
+ }
+ }
+
+ @Override
+ public void exit() {
+ // clear notifications
+ mConnectionParams = null;
+ mFailCause = null;
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case EVENT_CONNECT:
+ mRefCount++;
+ if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
+ if (msg.obj != null) {
+ notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
+ }
+ retVal = HANDLED;
+ break;
+ case EVENT_DISCONNECT:
+ mRefCount--;
+ if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
+ if (mRefCount == 0)
+ {
+ DisconnectParams dp = (DisconnectParams) msg.obj;
+ dp.tag = mTag;
+ tearDownData(dp);
+ transitionTo(mDisconnectingState);
+ } else {
+ if (msg.obj != null) {
+ notifyDisconnectCompleted((DisconnectParams) msg.obj, false);
+ }
+ }
+ retVal = HANDLED;
+ break;
+
+ case EVENT_DISCONNECT_ALL:
+ if (DBG) {
+ log("DcActiveState msg.what=EVENT_DISCONNECT_ALL RefCount=" + mRefCount);
+ }
+ mRefCount = 0;
+ DisconnectParams dp = (DisconnectParams) msg.obj;
+ dp.tag = mTag;
+ tearDownData(dp);
+ transitionTo(mDisconnectingState);
+ retVal = HANDLED;
+ break;
+
+ default:
+ if (VDBG) {
+ log("DcActiveState not handled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+ private DcActiveState mActiveState = new DcActiveState();
+
+ /**
+ * The state machine is disconnecting.
+ */
+ private class DcDisconnectingState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case EVENT_CONNECT:
+ if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
+ + mRefCount);
+ deferMessage(msg);
+ retVal = HANDLED;
+ break;
+
+ case EVENT_DEACTIVATE_DONE:
+ if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
+ AsyncResult ar = (AsyncResult) msg.obj;
+ DisconnectParams dp = (DisconnectParams) ar.userObj;
+ if (dp.tag == mTag) {
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
+ transitionTo(mInactiveState);
+ } else {
+ if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
+ + dp.tag + " mTag=" + mTag);
+ }
+ retVal = HANDLED;
+ break;
+
+ default:
+ if (VDBG) {
+ log("DcDisconnectingState not handled msg.what=0x" +
+ Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+ private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
+
+ /**
+ * The state machine is disconnecting after an creating a connection.
+ */
+ private class DcDisconnectionErrorCreatingConnection extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ boolean retVal;
+
+ switch (msg.what) {
+ case EVENT_DEACTIVATE_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ ConnectionParams cp = (ConnectionParams) ar.userObj;
+ if (cp.tag == mTag) {
+ if (DBG) {
+ log("DcDisconnectionErrorCreatingConnection" +
+ " msg.what=EVENT_DEACTIVATE_DONE");
+ }
+
+ // Transition to inactive but send notifications after
+ // we've entered the mInactive state.
+ mInactiveState.setEnterNotificationParams(cp,
+ FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1);
+ transitionTo(mInactiveState);
+ } else {
+ if (DBG) {
+ log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
+ " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
+ }
+ }
+ retVal = HANDLED;
+ break;
+
+ default:
+ if (VDBG) {
+ log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
+ + Integer.toHexString(msg.what));
+ }
+ retVal = NOT_HANDLED;
+ break;
+ }
+ return retVal;
+ }
+ }
+ private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
+ new DcDisconnectionErrorCreatingConnection();
+
+ // ******* public interface
+
+ /**
+ * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
+ * Used for cellular networks that use Acesss Point Names (APN) such
+ * as GSM networks.
+ *
+ * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+ * With AsyncResult.userObj set to the original msg.obj,
+ * AsyncResult.result = FailCause and AsyncResult.exception = Exception().
+ * @param apn is the Access Point Name to bring up a connection to
+ */
+ public void bringUp(Message onCompletedMsg, ApnSetting apn) {
+ sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
+ }
+
+ /**
+ * Tear down the connection through the apn on the network.
+ *
+ * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+ * With AsyncResult.userObj set to the original msg.obj.
+ */
+ public void tearDown(String reason, Message onCompletedMsg) {
+ sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
+ }
+
+ /**
+ * Tear down the connection through the apn on the network. Ignores refcount and
+ * and always tears down.
+ *
+ * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+ * With AsyncResult.userObj set to the original msg.obj.
+ */
+ public void tearDownAll(String reason, Message onCompletedMsg) {
+ sendMessage(obtainMessage(EVENT_DISCONNECT_ALL,
+ new DisconnectParams(reason, onCompletedMsg)));
+ }
+
+ /**
+ * @return the string for msg.what as our info.
+ */
+ @Override
+ protected String getWhatToString(int what) {
+ String info = null;
+ info = cmdToString(what);
+ if (info == null) {
+ info = DataConnectionAc.cmdToString(what);
+ }
+ return info;
+ }
+
+ /**
+ * Dump the current state.
+ *
+ * @param fd
+ * @param pw
+ * @param args
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print("DataConnection ");
+ super.dump(fd, pw, args);
+ pw.println(" mApnList=" + mApnList);
+ pw.flush();
+ pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
+ pw.println(" mApn=" + mApn);
+ pw.println(" mTag=" + mTag);
+ pw.flush();
+ pw.println(" phone=" + phone);
+ pw.println(" mRilVersion=" + mRilVersion);
+ pw.println(" cid=" + cid);
+ pw.flush();
+ pw.println(" mLinkProperties=" + mLinkProperties);
+ pw.flush();
+ pw.println(" mCapabilities=" + mCapabilities);
+ pw.println(" createTime=" + TimeUtils.logTimeOfDay(createTime));
+ pw.println(" lastFailTime=" + TimeUtils.logTimeOfDay(lastFailTime));
+ pw.println(" lastFailCause=" + lastFailCause);
+ pw.flush();
+ pw.println(" mRetryOverride=" + mRetryOverride);
+ pw.println(" mRefCount=" + mRefCount);
+ pw.println(" userData=" + userData);
+ if (mRetryMgr != null) pw.println(" " + mRetryMgr);
+ pw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DataConnectionAc.java b/src/java/com/android/internal/telephony/DataConnectionAc.java
new file mode 100644
index 0000000000000000000000000000000000000000..a24414ff3dd39fe249ce8553dae6e09f1a0b9e4f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DataConnectionAc.java
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import android.app.PendingIntent;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.ProxyProperties;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * AsyncChannel to a DataConnection
+ */
+public class DataConnectionAc extends AsyncChannel {
+ private static final boolean DBG = false;
+ private String mLogTag;
+
+ public DataConnection dataConnection;
+
+ public static final int BASE = Protocol.BASE_DATA_CONNECTION_AC;
+
+ public static final int REQ_IS_INACTIVE = BASE + 0;
+ public static final int RSP_IS_INACTIVE = BASE + 1;
+
+ public static final int REQ_GET_CID = BASE + 2;
+ public static final int RSP_GET_CID = BASE + 3;
+
+ public static final int REQ_GET_APNSETTING = BASE + 4;
+ public static final int RSP_GET_APNSETTING = BASE + 5;
+
+ public static final int REQ_GET_LINK_PROPERTIES = BASE + 6;
+ public static final int RSP_GET_LINK_PROPERTIES = BASE + 7;
+
+ public static final int REQ_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 8;
+ public static final int RSP_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 9;
+
+ public static final int REQ_GET_LINK_CAPABILITIES = BASE + 10;
+ public static final int RSP_GET_LINK_CAPABILITIES = BASE + 11;
+
+ public static final int REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 12;
+ public static final int RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 13;
+
+ public static final int REQ_RESET = BASE + 14;
+ public static final int RSP_RESET = BASE + 15;
+
+ public static final int REQ_GET_REFCOUNT = BASE + 16;
+ public static final int RSP_GET_REFCOUNT = BASE + 17;
+
+ public static final int REQ_ADD_APNCONTEXT = BASE + 18;
+ public static final int RSP_ADD_APNCONTEXT = BASE + 19;
+
+ public static final int REQ_REMOVE_APNCONTEXT = BASE + 20;
+ public static final int RSP_REMOVE_APNCONTEXT = BASE + 21;
+
+ public static final int REQ_GET_APNCONTEXT_LIST = BASE + 22;
+ public static final int RSP_GET_APNCONTEXT_LIST = BASE + 23;
+
+ public static final int REQ_SET_RECONNECT_INTENT = BASE + 24;
+ public static final int RSP_SET_RECONNECT_INTENT = BASE + 25;
+
+ public static final int REQ_GET_RECONNECT_INTENT = BASE + 26;
+ public static final int RSP_GET_RECONNECT_INTENT = BASE + 27;
+
+ private static final int CMD_TO_STRING_COUNT = RSP_GET_RECONNECT_INTENT - BASE + 1;
+ private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
+ static {
+ sCmdToString[REQ_IS_INACTIVE - BASE] = "REQ_IS_INACTIVE";
+ sCmdToString[RSP_IS_INACTIVE - BASE] = "RSP_IS_INACTIVE";
+ sCmdToString[REQ_GET_CID - BASE] = "REQ_GET_CID";
+ sCmdToString[RSP_GET_CID - BASE] = "RSP_GET_CID";
+ sCmdToString[REQ_GET_APNSETTING - BASE] = "REQ_GET_APNSETTING";
+ sCmdToString[RSP_GET_APNSETTING - BASE] = "RSP_GET_APNSETTING";
+ sCmdToString[REQ_GET_LINK_PROPERTIES - BASE] = "REQ_GET_LINK_PROPERTIES";
+ sCmdToString[RSP_GET_LINK_PROPERTIES - BASE] = "RSP_GET_LINK_PROPERTIES";
+ sCmdToString[REQ_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+ "REQ_SET_LINK_PROPERTIES_HTTP_PROXY";
+ sCmdToString[RSP_SET_LINK_PROPERTIES_HTTP_PROXY - BASE] =
+ "RSP_SET_LINK_PROPERTIES_HTTP_PROXY";
+ sCmdToString[REQ_GET_LINK_CAPABILITIES - BASE] = "REQ_GET_LINK_CAPABILITIES";
+ sCmdToString[RSP_GET_LINK_CAPABILITIES - BASE] = "RSP_GET_LINK_CAPABILITIES";
+ sCmdToString[REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+ "REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+ sCmdToString[RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE - BASE] =
+ "RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE";
+ sCmdToString[REQ_RESET - BASE] = "REQ_RESET";
+ sCmdToString[RSP_RESET - BASE] = "RSP_RESET";
+ sCmdToString[REQ_GET_REFCOUNT - BASE] = "REQ_GET_REFCOUNT";
+ sCmdToString[RSP_GET_REFCOUNT - BASE] = "RSP_GET_REFCOUNT";
+ sCmdToString[REQ_ADD_APNCONTEXT - BASE] = "REQ_ADD_APNCONTEXT";
+ sCmdToString[RSP_ADD_APNCONTEXT - BASE] = "RSP_ADD_APNCONTEXT";
+ sCmdToString[REQ_REMOVE_APNCONTEXT - BASE] = "REQ_REMOVE_APNCONTEXT";
+ sCmdToString[RSP_REMOVE_APNCONTEXT - BASE] = "RSP_REMOVE_APNCONTEXT";
+ sCmdToString[REQ_GET_APNCONTEXT_LIST - BASE] = "REQ_GET_APNCONTEXT_LIST";
+ sCmdToString[RSP_GET_APNCONTEXT_LIST - BASE] = "RSP_GET_APNCONTEXT_LIST";
+ sCmdToString[REQ_SET_RECONNECT_INTENT - BASE] = "REQ_SET_RECONNECT_INTENT";
+ sCmdToString[RSP_SET_RECONNECT_INTENT - BASE] = "RSP_SET_RECONNECT_INTENT";
+ sCmdToString[REQ_GET_RECONNECT_INTENT - BASE] = "REQ_GET_RECONNECT_INTENT";
+ sCmdToString[RSP_GET_RECONNECT_INTENT - BASE] = "RSP_GET_RECONNECT_INTENT";
+ }
+ protected static String cmdToString(int cmd) {
+ cmd -= BASE;
+ if ((cmd >= 0) && (cmd < sCmdToString.length)) {
+ return sCmdToString[cmd];
+ } else {
+ return AsyncChannel.cmdToString(cmd + BASE);
+ }
+ }
+
+ /**
+ * enum used to notify action taken or necessary to be
+ * taken after the link property is changed.
+ */
+ public enum LinkPropertyChangeAction {
+ NONE, CHANGED, RESET;
+
+ public static LinkPropertyChangeAction fromInt(int value) {
+ if (value == NONE.ordinal()) {
+ return NONE;
+ } else if (value == CHANGED.ordinal()) {
+ return CHANGED;
+ } else if (value == RESET.ordinal()) {
+ return RESET;
+ } else {
+ throw new RuntimeException("LinkPropertyChangeAction.fromInt: bad value=" + value);
+ }
+ }
+ }
+
+ public DataConnectionAc(DataConnection dc, String logTag) {
+ dataConnection = dc;
+ mLogTag = logTag;
+ }
+
+ /**
+ * Request if the state machine is in the inactive state.
+ * Response {@link #rspIsInactive}
+ */
+ public void reqIsInactive() {
+ sendMessage(REQ_IS_INACTIVE);
+ if (DBG) log("reqIsInactive");
+ }
+
+ /**
+ * Evaluate RSP_IS_INACTIVE.
+ *
+ * @return true if the state machine is in the inactive state.
+ */
+ public boolean rspIsInactive(Message response) {
+ boolean retVal = response.arg1 == 1;
+ if (DBG) log("rspIsInactive=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return true if the state machine is in the inactive state.
+ */
+ public boolean isInactiveSync() {
+ Message response = sendMessageSynchronously(REQ_IS_INACTIVE);
+ if ((response != null) && (response.what == RSP_IS_INACTIVE)) {
+ return rspIsInactive(response);
+ } else {
+ log("rspIsInactive error response=" + response);
+ return false;
+ }
+ }
+
+ /**
+ * Request the Connection ID.
+ * Response {@link #rspCid}
+ */
+ public void reqCid() {
+ sendMessage(REQ_GET_CID);
+ if (DBG) log("reqCid");
+ }
+
+ /**
+ * Evaluate a RSP_GET_CID message and return the cid.
+ *
+ * @param response Message
+ * @return connection id or -1 if an error
+ */
+ public int rspCid(Message response) {
+ int retVal = response.arg1;
+ if (DBG) log("rspCid=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return connection id or -1 if an error
+ */
+ public int getCidSync() {
+ Message response = sendMessageSynchronously(REQ_GET_CID);
+ if ((response != null) && (response.what == RSP_GET_CID)) {
+ return rspCid(response);
+ } else {
+ log("rspCid error response=" + response);
+ return -1;
+ }
+ }
+
+ /**
+ * Request the Reference Count.
+ * Response {@link #rspRefCount}
+ */
+ public void reqRefCount() {
+ sendMessage(REQ_GET_REFCOUNT);
+ if (DBG) log("reqRefCount");
+ }
+
+ /**
+ * Evaluate a RSP_GET_REFCOUNT message and return the refCount.
+ *
+ * @param response Message
+ * @return ref count or -1 if an error
+ */
+ public int rspRefCount(Message response) {
+ int retVal = response.arg1;
+ if (DBG) log("rspRefCount=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return connection id or -1 if an error
+ */
+ public int getRefCountSync() {
+ Message response = sendMessageSynchronously(REQ_GET_REFCOUNT);
+ if ((response != null) && (response.what == RSP_GET_REFCOUNT)) {
+ return rspRefCount(response);
+ } else {
+ log("rspRefCount error response=" + response);
+ return -1;
+ }
+ }
+
+ /**
+ * Request the connections ApnSetting.
+ * Response {@link #rspApnSetting}
+ */
+ public void reqApnSetting() {
+ sendMessage(REQ_GET_APNSETTING);
+ if (DBG) log("reqApnSetting");
+ }
+
+ /**
+ * Evaluate a RSP_APN_SETTING message and return the ApnSetting.
+ *
+ * @param response Message
+ * @return ApnSetting, maybe null
+ */
+ public ApnSetting rspApnSetting(Message response) {
+ ApnSetting retVal = (ApnSetting) response.obj;
+ if (DBG) log("rspApnSetting=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * Get the connections ApnSetting.
+ *
+ * @return ApnSetting or null if an error
+ */
+ public ApnSetting getApnSettingSync() {
+ Message response = sendMessageSynchronously(REQ_GET_APNSETTING);
+ if ((response != null) && (response.what == RSP_GET_APNSETTING)) {
+ return rspApnSetting(response);
+ } else {
+ log("getApnSetting error response=" + response);
+ return null;
+ }
+ }
+
+ /**
+ * Request the connections LinkProperties.
+ * Response {@link #rspLinkProperties}
+ */
+ public void reqLinkProperties() {
+ sendMessage(REQ_GET_LINK_PROPERTIES);
+ if (DBG) log("reqLinkProperties");
+ }
+
+ /**
+ * Evaluate RSP_GET_LINK_PROPERTIES
+ *
+ * @param response
+ * @return LinkProperties, maybe null.
+ */
+ public LinkProperties rspLinkProperties(Message response) {
+ LinkProperties retVal = (LinkProperties) response.obj;
+ if (DBG) log("rspLinkProperties=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * Get the connections LinkProperties.
+ *
+ * @return LinkProperties or null if an error
+ */
+ public LinkProperties getLinkPropertiesSync() {
+ Message response = sendMessageSynchronously(REQ_GET_LINK_PROPERTIES);
+ if ((response != null) && (response.what == RSP_GET_LINK_PROPERTIES)) {
+ return rspLinkProperties(response);
+ } else {
+ log("getLinkProperties error response=" + response);
+ return null;
+ }
+ }
+
+ /**
+ * Request setting the connections LinkProperties.HttpProxy.
+ * Response RSP_SET_LINK_PROPERTIES when complete.
+ */
+ public void reqSetLinkPropertiesHttpProxy(ProxyProperties proxy) {
+ sendMessage(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+ if (DBG) log("reqSetLinkPropertiesHttpProxy proxy=" + proxy);
+ }
+
+ /**
+ * Set the connections LinkProperties.HttpProxy
+ */
+ public void setLinkPropertiesHttpProxySync(ProxyProperties proxy) {
+ Message response =
+ sendMessageSynchronously(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+ if ((response != null) && (response.what == RSP_SET_LINK_PROPERTIES_HTTP_PROXY)) {
+ if (DBG) log("setLinkPropertiesHttpPoxy ok");
+ } else {
+ log("setLinkPropertiesHttpPoxy error response=" + response);
+ }
+ }
+
+ /**
+ * Request update LinkProperties from DataCallState
+ * Response {@link #rspUpdateLinkPropertiesDataCallState}
+ */
+ public void reqUpdateLinkPropertiesDataCallState(DataCallState newState) {
+ sendMessage(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+ if (DBG) log("reqUpdateLinkPropertiesDataCallState");
+ }
+
+ public UpdateLinkPropertyResult rspUpdateLinkPropertiesDataCallState(Message response) {
+ UpdateLinkPropertyResult retVal = (UpdateLinkPropertyResult)response.obj;
+ if (DBG) log("rspUpdateLinkPropertiesState: retVal=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * Update link properties in the data connection
+ *
+ * @return the removed and added addresses.
+ */
+ public UpdateLinkPropertyResult updateLinkPropertiesDataCallStateSync(DataCallState newState) {
+ Message response =
+ sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+ if ((response != null) &&
+ (response.what == RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE)) {
+ return rspUpdateLinkPropertiesDataCallState(response);
+ } else {
+ log("getLinkProperties error response=" + response);
+ return new UpdateLinkPropertyResult(new LinkProperties());
+ }
+ }
+
+ /**
+ * Request the connections LinkCapabilities.
+ * Response {@link #rspLinkCapabilities}
+ */
+ public void reqLinkCapabilities() {
+ sendMessage(REQ_GET_LINK_CAPABILITIES);
+ if (DBG) log("reqLinkCapabilities");
+ }
+
+ /**
+ * Evaluate RSP_GET_LINK_CAPABILITIES
+ *
+ * @param response
+ * @return LinkCapabilites, maybe null.
+ */
+ public LinkCapabilities rspLinkCapabilities(Message response) {
+ LinkCapabilities retVal = (LinkCapabilities) response.obj;
+ if (DBG) log("rspLinkCapabilities=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * Get the connections LinkCapabilities.
+ *
+ * @return LinkCapabilities or null if an error
+ */
+ public LinkCapabilities getLinkCapabilitiesSync() {
+ Message response = sendMessageSynchronously(REQ_GET_LINK_CAPABILITIES);
+ if ((response != null) && (response.what == RSP_GET_LINK_CAPABILITIES)) {
+ return rspLinkCapabilities(response);
+ } else {
+ log("getLinkCapabilities error response=" + response);
+ return null;
+ }
+ }
+
+ /**
+ * Request the connections LinkCapabilities.
+ * Response RSP_RESET when complete
+ */
+ public void reqReset() {
+ sendMessage(REQ_RESET);
+ if (DBG) log("reqReset");
+ }
+
+ /**
+ * Reset the connection and wait for it to complete.
+ */
+ public void resetSync() {
+ Message response = sendMessageSynchronously(REQ_RESET);
+ if ((response != null) && (response.what == RSP_RESET)) {
+ if (DBG) log("restSync ok");
+ } else {
+ log("restSync error response=" + response);
+ }
+ }
+
+ /**
+ * Request to add ApnContext association.
+ * Response RSP_ADD_APNCONTEXT when complete.
+ */
+ public void reqAddApnContext(ApnContext apnContext) {
+ Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
+ if (DBG) log("reqAddApnContext");
+ }
+
+ /**
+ * Add ApnContext association synchronoulsy.
+ *
+ * @param ApnContext to associate
+ */
+ public void addApnContextSync(ApnContext apnContext) {
+ Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
+ if ((response != null) && (response.what == RSP_ADD_APNCONTEXT)) {
+ if (DBG) log("addApnContext ok");
+ } else {
+ log("addApnContext error response=" + response);
+ }
+ }
+
+ /**
+ * Request to remove ApnContext association.
+ * Response RSP_REMOVE_APNCONTEXT when complete.
+ */
+ public void reqRemomveApnContext(ApnContext apnContext) {
+ Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
+ if (DBG) log("reqRemomveApnContext");
+ }
+
+ /**
+ * Remove ApnContext associateion.
+ *
+ * @param ApnContext to dissociate
+ */
+ public void removeApnContextSync(ApnContext apnContext) {
+ Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
+ if ((response != null) && (response.what == RSP_REMOVE_APNCONTEXT)) {
+ if (DBG) log("removeApnContext ok");
+ } else {
+ log("removeApnContext error response=" + response);
+ }
+ }
+
+ /**
+ * Request to retrive ApnContext List associated with DC.
+ * Response RSP_GET_APNCONTEXT_LIST when complete.
+ */
+ public void reqGetApnList(ApnContext apnContext) {
+ Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
+ if (DBG) log("reqGetApnList");
+ }
+
+ /**
+ * Retrieve Collection of ApnContext from the response message.
+ *
+ * @param Message sent from DC in response to REQ_GET_APNCONTEXT_LIST.
+ * @return Collection of ApnContext
+ */
+ public Collection rspApnList(Message response) {
+ Collection retVal = (Collection)response.obj;
+ if (retVal == null) retVal = new ArrayList();
+ return retVal;
+ }
+
+ /**
+ * Retrieve collection of ApnContext currently associated with
+ * the DataConnectionA synchronously.
+ *
+ * @return Collection of ApnContext
+ */
+ public Collection getApnListSync() {
+ Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
+ if ((response != null) && (response.what == RSP_GET_APNCONTEXT_LIST)) {
+ if (DBG) log("getApnList ok");
+ return rspApnList(response);
+ } else {
+ log("getApnList error response=" + response);
+ // return dummy list with no entry
+ return new ArrayList();
+ }
+ }
+
+ /**
+ * Request to set Pending ReconnectIntent to DC.
+ * Response RSP_SET_RECONNECT_INTENT when complete.
+ */
+ public void reqSetReconnectIntent(PendingIntent intent) {
+ Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
+ if (DBG) log("reqSetReconnectIntent");
+ }
+
+ /**
+ * Set pending reconnect intent to DC synchronously.
+ *
+ * @param PendingIntent to set.
+ */
+ public void setReconnectIntentSync(PendingIntent intent) {
+ Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
+ if ((response != null) && (response.what == RSP_SET_RECONNECT_INTENT)) {
+ if (DBG) log("setReconnectIntent ok");
+ } else {
+ log("setReconnectIntent error response=" + response);
+ }
+ }
+
+ /**
+ * Request to get Pending ReconnectIntent to DC.
+ * Response RSP_GET_RECONNECT_INTENT when complete.
+ */
+ public void reqGetReconnectIntent() {
+ Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
+ if (DBG) log("reqGetReconnectIntent");
+ }
+
+ /**
+ * Retrieve reconnect intent from response message from DC.
+ *
+ * @param Message which contains the reconnect intent.
+ * @return PendingIntent from the response.
+ */
+ public PendingIntent rspReconnectIntent(Message response) {
+ PendingIntent retVal = (PendingIntent) response.obj;
+ return retVal;
+ }
+
+ /**
+ * Retrieve reconnect intent currently set in DC synchronously.
+ *
+ * @return PendingIntent reconnect intent current ly set in DC
+ */
+ public PendingIntent getReconnectIntentSync() {
+ Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
+ if ((response != null) && (response.what == RSP_GET_RECONNECT_INTENT)) {
+ if (DBG) log("getReconnectIntent ok");
+ return rspReconnectIntent(response);
+ } else {
+ log("getReconnectIntent error response=" + response);
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return dataConnection.getName();
+ }
+
+ private void log(String s) {
+ android.util.Log.d(mLogTag, "DataConnectionAc " + s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DataConnectionTracker.java b/src/java/com/android/internal/telephony/DataConnectionTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..89a02d3e64b69da62f8b98c95de433b1e587b60a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -0,0 +1,1203 @@
+/*
+ * 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.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.TrafficStats;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.telephony.DataConnection.FailCause;
+import com.android.internal.telephony.DctConstants;
+import com.android.internal.util.AsyncChannel;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * {@hide}
+ */
+public abstract class DataConnectionTracker extends Handler {
+ protected static final boolean DBG = true;
+ protected static final boolean VDBG = false;
+
+
+ /** Delay between APN attempts.
+ Note the property override mechanism is there just for testing purpose only. */
+ protected static final int APN_DELAY_MILLIS =
+ SystemProperties.getInt("persist.radio.apn_delay", 5000);
+
+ protected Object mDataEnabledLock = new Object();
+
+ // responds to the setInternalDataEnabled call - used internally to turn off data
+ // for example during emergency calls
+ protected boolean mInternalDataEnabled = true;
+
+ // responds to public (user) API to enable/disable data use
+ // independent of mInternalDataEnabled and requests for APN access
+ // persisted
+ protected boolean mUserDataEnabled = true;
+
+ // TODO: move away from static state once 5587429 is fixed.
+ protected static boolean sPolicyDataEnabled = true;
+
+ private boolean[] dataEnabled = new boolean[DctConstants.APN_NUM_TYPES];
+
+ private int enabledCount = 0;
+
+ /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
+ protected String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+
+ /** Retry configuration: A doubling of retry times from 5secs to 30minutes */
+ protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000,"
+ + "5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000";
+
+ /** Retry configuration for secondary networks: 4 tries in 20 sec */
+ protected static final String SECONDARY_DATA_RETRY_CONFIG =
+ "max_retries=3, 5000, 5000, 5000";
+
+ /** Slow poll when attempting connection recovery. */
+ protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
+ /** Default max failure count before attempting to network re-registration. */
+ protected static final int DEFAULT_MAX_PDP_RESET_FAIL = 3;
+
+ /**
+ * After detecting a potential connection problem, this is the max number
+ * of subsequent polls before attempting recovery.
+ */
+ protected static final int NO_RECV_POLL_LIMIT = 24;
+ // 1 sec. default polling interval when screen is on.
+ protected static final int POLL_NETSTAT_MILLIS = 1000;
+ // 10 min. default polling interval when screen is off.
+ protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
+ // 2 min for round trip time
+ protected static final int POLL_LONGEST_RTT = 120 * 1000;
+ // Default sent packets without ack which triggers initial recovery steps
+ protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
+ // how long to wait before switching back to default APN
+ protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000;
+ // system property that can override the above value
+ protected static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore";
+ // represents an invalid IP address
+ protected static final String NULL_IP = "0.0.0.0";
+
+ // Default for the data stall alarm while non-aggressive stall detection
+ protected static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
+ // Default for the data stall alarm for aggressive stall detection
+ protected static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;
+ // If attempt is less than this value we're doing first level recovery
+ protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1;
+ // Tag for tracking stale alarms
+ protected static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag";
+
+ // TODO: See if we can remove INTENT_RECONNECT_ALARM
+ // having to have different values for GSM and
+ // CDMA. If so we can then remove the need for
+ // getActionIntentReconnectAlarm.
+ protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON =
+ "reconnect_alarm_extra_reason";
+
+ // Used for debugging. Send the INTENT with an optional counter value with the number
+ // of times the setup is to fail before succeeding. If the counter isn't passed the
+ // setup will fail once. Example fail two times with FailCause.SIGNAL_LOST(-3)
+ // adb shell am broadcast \
+ // -a com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter \
+ // --ei fail_data_setup_counter 3 --ei fail_data_setup_fail_cause -3
+ protected static final String INTENT_SET_FAIL_DATA_SETUP_COUNTER =
+ "com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter";
+ protected static final String FAIL_DATA_SETUP_COUNTER = "fail_data_setup_counter";
+ protected int mFailDataSetupCounter = 0;
+ protected static final String FAIL_DATA_SETUP_FAIL_CAUSE = "fail_data_setup_fail_cause";
+ protected FailCause mFailDataSetupFailCause = FailCause.ERROR_UNSPECIFIED;
+
+ protected static final String DEFALUT_DATA_ON_BOOT_PROP = "net.def_data_on_boot";
+
+ // member variables
+ protected PhoneBase mPhone;
+ protected DctConstants.Activity mActivity = DctConstants.Activity.NONE;
+ protected DctConstants.State mState = DctConstants.State.IDLE;
+ protected Handler mDataConnectionTracker = null;
+
+
+ protected long mTxPkts;
+ protected long mRxPkts;
+ protected int mNetStatPollPeriod;
+ protected boolean mNetStatPollEnabled = false;
+
+ protected TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
+ // Used to track stale data stall alarms.
+ protected int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
+ // The current data stall alarm intent
+ protected PendingIntent mDataStallAlarmIntent = null;
+ // Number of packets sent since the last received packet
+ protected long mSentSinceLastRecv;
+ // Controls when a simple recovery attempt it to be tried
+ protected int mNoRecvPollCount = 0;
+
+ // wifi connection status will be updated by sticky intent
+ protected boolean mIsWifiConnected = false;
+
+ /** Intent sent when the reconnect alarm fires. */
+ protected PendingIntent mReconnectIntent = null;
+
+ /** CID of active data connection */
+ protected int mCidActive;
+
+ // When false we will not auto attach and manually attaching is required.
+ protected boolean mAutoAttachOnCreation = false;
+
+ // State of screen
+ // (TODO: Reconsider tying directly to screen, maybe this is
+ // really a lower power mode")
+ protected boolean mIsScreenOn = true;
+
+ /** Allows the generation of unique Id's for DataConnection objects */
+ protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
+
+ /** The data connections. */
+ protected HashMap mDataConnections =
+ new HashMap();
+
+ /** The data connection async channels */
+ protected HashMap mDataConnectionAsyncChannels =
+ new HashMap();
+
+ /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
+ protected HashMap mApnToDataConnectionId =
+ new HashMap();
+
+ /** Phone.APN_TYPE_* ===> ApnContext */
+ protected ConcurrentHashMap mApnContexts =
+ new ConcurrentHashMap();
+
+ /* Currently active APN */
+ protected ApnSetting mActiveApn;
+
+ /** allApns holds all apns */
+ protected ArrayList mAllApns = null;
+
+ /** preferred apn */
+ protected ApnSetting mPreferredApn = null;
+
+ /** Is packet service restricted by network */
+ protected boolean mIsPsRestricted = false;
+
+ /* Once disposed dont handle any messages */
+ protected boolean mIsDisposed = false;
+
+ protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ String action = intent.getAction();
+ if (DBG) log("onReceive: action=" + action);
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ mIsScreenOn = true;
+ stopNetStatPoll();
+ startNetStatPoll();
+ restartDataStallAlarm();
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ mIsScreenOn = false;
+ stopNetStatPoll();
+ startNetStatPoll();
+ restartDataStallAlarm();
+ } else if (action.startsWith(getActionIntentReconnectAlarm())) {
+ log("Reconnect alarm. Previous state was " + mState);
+ onActionIntentReconnectAlarm(intent);
+ } else if (action.equals(getActionIntentDataStallAlarm())) {
+ onActionIntentDataStallAlarm(intent);
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ final android.net.NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
+ } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+ if (!enabled) {
+ // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
+ // quit and won't report disconnected until next enabling.
+ mIsWifiConnected = false;
+ }
+ } else if (action.equals(INTENT_SET_FAIL_DATA_SETUP_COUNTER)) {
+ mFailDataSetupCounter = intent.getIntExtra(FAIL_DATA_SETUP_COUNTER, 1);
+ mFailDataSetupFailCause = FailCause.fromInt(
+ intent.getIntExtra(FAIL_DATA_SETUP_FAIL_CAUSE,
+ FailCause.ERROR_UNSPECIFIED.getErrorCode()));
+ if (DBG) log("set mFailDataSetupCounter=" + mFailDataSetupCounter +
+ " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
+ }
+ }
+ };
+
+ private final DataRoamingSettingObserver mDataRoamingSettingObserver;
+
+ private class DataRoamingSettingObserver extends ContentObserver {
+ public DataRoamingSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.DATA_ROAMING), false, this);
+ }
+
+ public void unregister(Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ resolver.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ // already running on mPhone handler thread
+ handleDataOnRoamingChange();
+ }
+ }
+
+ /**
+ * Maintian the sum of transmit and receive packets.
+ *
+ * The packet counts are initizlied and reset to -1 and
+ * remain -1 until they can be updated.
+ */
+ public class TxRxSum {
+ public long txPkts;
+ public long rxPkts;
+
+ public TxRxSum() {
+ reset();
+ }
+
+ public TxRxSum(long txPkts, long rxPkts) {
+ this.txPkts = txPkts;
+ this.rxPkts = rxPkts;
+ }
+
+ public TxRxSum(TxRxSum sum) {
+ txPkts = sum.txPkts;
+ rxPkts = sum.rxPkts;
+ }
+
+ public void reset() {
+ txPkts = -1;
+ rxPkts = -1;
+ }
+
+ public String toString() {
+ return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
+ }
+
+ public void updateTxRxSum() {
+ boolean txUpdated = false, rxUpdated = false;
+ long txSum = 0, rxSum = 0;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.getState() == DctConstants.State.CONNECTED) {
+ DataConnectionAc dcac = apnContext.getDataConnectionAc();
+ if (dcac == null) continue;
+
+ LinkProperties linkProp = dcac.getLinkPropertiesSync();
+ if (linkProp == null) continue;
+
+ String iface = linkProp.getInterfaceName();
+
+ if (iface != null) {
+ long stats = TrafficStats.getTxPackets(iface);
+ if (stats > 0) {
+ txUpdated = true;
+ txSum += stats;
+ }
+ stats = TrafficStats.getRxPackets(iface);
+ if (stats > 0) {
+ rxUpdated = true;
+ rxSum += stats;
+ }
+ }
+ }
+ }
+ if (txUpdated) this.txPkts = txSum;
+ if (rxUpdated) this.rxPkts = rxSum;
+ }
+ }
+
+ protected boolean isDataSetupCompleteOk(AsyncResult ar) {
+ if (ar.exception != null) {
+ if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
+ return false;
+ }
+ if (mFailDataSetupCounter <= 0) {
+ if (DBG) log("isDataSetupCompleteOk return true");
+ return true;
+ }
+ ar.result = mFailDataSetupFailCause;
+ if (DBG) {
+ log("isDataSetupCompleteOk return false" +
+ " mFailDataSetupCounter=" + mFailDataSetupCounter +
+ " mFailDataSetupFailCause=" + mFailDataSetupFailCause);
+ }
+ mFailDataSetupCounter -= 1;
+ return false;
+ }
+
+ protected void onActionIntentReconnectAlarm(Intent intent) {
+ String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
+ if (mState == DctConstants.State.FAILED) {
+ Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION);
+ msg.arg1 = 0; // tearDown is false
+ msg.arg2 = 0;
+ msg.obj = reason;
+ sendMessage(msg);
+ }
+ sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA));
+ }
+
+ protected void onActionIntentDataStallAlarm(Intent intent) {
+ if (VDBG) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
+ Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM,
+ intent.getAction());
+ msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0);
+ sendMessage(msg);
+ }
+
+ /**
+ * Default constructor
+ */
+ protected DataConnectionTracker(PhoneBase phone) {
+ super();
+ if (DBG) log("DCT.constructor");
+ mPhone = phone;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(getActionIntentReconnectAlarm());
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
+
+ mUserDataEnabled = Settings.Secure.getInt(
+ mPhone.getContext().getContentResolver(), Settings.Secure.MOBILE_DATA, 1) == 1;
+
+ // TODO: Why is this registering the phone as the receiver of the intent
+ // and not its own handler?
+ mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
+
+ // This preference tells us 1) initial condition for "dataEnabled",
+ // and 2) whether the RIL will setup the baseband to auto-PS attach.
+
+ dataEnabled[DctConstants.APN_DEFAULT_ID] =
+ SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP,true);
+ if (dataEnabled[DctConstants.APN_DEFAULT_ID]) {
+ enabledCount++;
+ }
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
+ mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
+
+ // watch for changes to Settings.Secure.DATA_ROAMING
+ mDataRoamingSettingObserver = new DataRoamingSettingObserver(mPhone);
+ mDataRoamingSettingObserver.register(mPhone.getContext());
+ }
+
+ public void dispose() {
+ if (DBG) log("DCT.dispose");
+ for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+ dcac.disconnect();
+ }
+ mDataConnectionAsyncChannels.clear();
+ mIsDisposed = true;
+ mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
+ mDataRoamingSettingObserver.unregister(mPhone.getContext());
+ }
+
+ protected void broadcastMessenger() {
+ Intent intent = new Intent(DctConstants.ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
+ intent.putExtra(DctConstants.EXTRA_MESSENGER, new Messenger(this));
+ mPhone.getContext().sendBroadcast(intent);
+ }
+
+ public DctConstants.Activity getActivity() {
+ return mActivity;
+ }
+
+ public boolean isApnTypeActive(String type) {
+ // TODO: support simultaneous with List instead
+ if (PhoneConstants.APN_TYPE_DUN.equals(type)) {
+ ApnSetting dunApn = fetchDunApn();
+ if (dunApn != null) {
+ return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString())));
+ }
+ }
+ return mActiveApn != null && mActiveApn.canHandleType(type);
+ }
+
+ protected ApnSetting fetchDunApn() {
+ if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
+ log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
+ return null;
+ }
+ Context c = mPhone.getContext();
+ String apnData = Settings.Secure.getString(c.getContentResolver(),
+ Settings.Secure.TETHER_DUN_APN);
+ ApnSetting dunSetting = ApnSetting.fromString(apnData);
+ if (dunSetting != null) {
+ if (VDBG) log("fetchDunApn: secure TETHER_DUN_APN dunSetting=" + dunSetting);
+ return dunSetting;
+ }
+
+ apnData = c.getResources().getString(R.string.config_tether_apndata);
+ dunSetting = ApnSetting.fromString(apnData);
+ if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + dunSetting);
+ return dunSetting;
+ }
+
+ public String[] getActiveApnTypes() {
+ String[] result;
+ if (mActiveApn != null) {
+ result = mActiveApn.types;
+ } else {
+ result = new String[1];
+ result[0] = PhoneConstants.APN_TYPE_DEFAULT;
+ }
+ return result;
+ }
+
+ /** TODO: See if we can remove */
+ public String getActiveApnString(String apnType) {
+ String result = null;
+ if (mActiveApn != null) {
+ result = mActiveApn.apn;
+ }
+ return result;
+ }
+
+ /**
+ * Modify {@link Settings.Secure#DATA_ROAMING} value.
+ */
+ public void setDataOnRoamingEnabled(boolean enabled) {
+ if (getDataOnRoamingEnabled() != enabled) {
+ final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ Settings.Secure.putInt(resolver, Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
+ // will trigger handleDataOnRoamingChange() through observer
+ }
+ }
+
+ /**
+ * Return current {@link Settings.Secure#DATA_ROAMING} value.
+ */
+ public boolean getDataOnRoamingEnabled() {
+ try {
+ final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ return Settings.Secure.getInt(resolver, Settings.Secure.DATA_ROAMING) != 0;
+ } catch (SettingNotFoundException snfe) {
+ return false;
+ }
+ }
+
+ private void handleDataOnRoamingChange() {
+ if (mPhone.getServiceState().getRoaming()) {
+ if (getDataOnRoamingEnabled()) {
+ resetAllRetryCounts();
+ }
+ sendMessage(obtainMessage(DctConstants.EVENT_ROAMING_ON));
+ }
+ }
+
+ // abstract methods
+ protected abstract String getActionIntentReconnectAlarm();
+ protected abstract String getActionIntentDataStallAlarm();
+ protected abstract void startNetStatPoll();
+ protected abstract void stopNetStatPoll();
+ protected abstract void restartDataStallAlarm();
+ protected abstract void restartRadio();
+ protected abstract void log(String s);
+ protected abstract void loge(String s);
+ protected abstract boolean isDataAllowed();
+ protected abstract boolean isApnTypeAvailable(String type);
+ public abstract DctConstants.State getState(String apnType);
+ protected abstract void setState(DctConstants.State s);
+ protected abstract void gotoIdleAndNotifyDataConnection(String reason);
+
+ protected abstract boolean onTrySetupData(String reason);
+ protected abstract void onRoamingOff();
+ protected abstract void onRoamingOn();
+ protected abstract void onRadioAvailable();
+ protected abstract void onRadioOffOrNotAvailable();
+ protected abstract void onDataSetupComplete(AsyncResult ar);
+ protected abstract void onDisconnectDone(int connId, AsyncResult ar);
+ protected abstract void onVoiceCallStarted();
+ protected abstract void onVoiceCallEnded();
+ protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason);
+ protected abstract void onCleanUpAllConnections(String cause);
+ protected abstract boolean isDataPossible(String apnType);
+
+ protected void onDataStallAlarm(int tag) {
+ loge("onDataStallAlarm: not impleted tag=" + tag);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ log("DISCONNECTED_CONNECTED: msg=" + msg);
+ DataConnectionAc dcac = (DataConnectionAc) msg.obj;
+ mDataConnectionAsyncChannels.remove(dcac.dataConnection.getDataConnectionId());
+ dcac.disconnected();
+ break;
+ }
+ case DctConstants.EVENT_ENABLE_NEW_APN:
+ onEnableApn(msg.arg1, msg.arg2);
+ break;
+
+ case DctConstants.EVENT_TRY_SETUP_DATA:
+ String reason = null;
+ if (msg.obj instanceof String) {
+ reason = (String) msg.obj;
+ }
+ onTrySetupData(reason);
+ break;
+
+ case DctConstants.EVENT_DATA_STALL_ALARM:
+ onDataStallAlarm(msg.arg1);
+ break;
+
+ case DctConstants.EVENT_ROAMING_OFF:
+ if (getDataOnRoamingEnabled() == false) {
+ resetAllRetryCounts();
+ }
+ onRoamingOff();
+ break;
+
+ case DctConstants.EVENT_ROAMING_ON:
+ onRoamingOn();
+ break;
+
+ case DctConstants.EVENT_RADIO_AVAILABLE:
+ onRadioAvailable();
+ break;
+
+ case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
+ onRadioOffOrNotAvailable();
+ break;
+
+ case DctConstants.EVENT_DATA_SETUP_COMPLETE:
+ mCidActive = msg.arg1;
+ onDataSetupComplete((AsyncResult) msg.obj);
+ break;
+
+ case DctConstants.EVENT_DISCONNECT_DONE:
+ log("DataConnectoinTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg);
+ onDisconnectDone(msg.arg1, (AsyncResult) msg.obj);
+ break;
+
+ case DctConstants.EVENT_VOICE_CALL_STARTED:
+ onVoiceCallStarted();
+ break;
+
+ case DctConstants.EVENT_VOICE_CALL_ENDED:
+ onVoiceCallEnded();
+ break;
+
+ case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS: {
+ onCleanUpAllConnections((String) msg.obj);
+ break;
+ }
+ case DctConstants.EVENT_CLEAN_UP_CONNECTION: {
+ boolean tearDown = (msg.arg1 == 0) ? false : true;
+ onCleanUpConnection(tearDown, msg.arg2, (String) msg.obj);
+ break;
+ }
+ case DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE: {
+ boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
+ onSetInternalDataEnabled(enabled);
+ break;
+ }
+ case DctConstants.EVENT_RESET_DONE: {
+ if (DBG) log("EVENT_RESET_DONE");
+ onResetDone((AsyncResult) msg.obj);
+ break;
+ }
+ case DctConstants.CMD_SET_USER_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
+ if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
+ onSetUserDataEnabled(enabled);
+ break;
+ }
+ case DctConstants.CMD_SET_DEPENDENCY_MET: {
+ boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
+ if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
+ Bundle bundle = msg.getData();
+ if (bundle != null) {
+ String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
+ if (apnType != null) {
+ onSetDependencyMet(apnType, met);
+ }
+ }
+ break;
+ }
+ case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
+ onSetPolicyDataEnabled(enabled);
+ break;
+ }
+ default:
+ Log.e("DATA", "Unidentified event msg=" + msg);
+ break;
+ }
+ }
+
+ /**
+ * Report on whether data connectivity is enabled
+ *
+ * @return {@code false} if data connectivity has been explicitly disabled,
+ * {@code true} otherwise.
+ */
+ public boolean getAnyDataEnabled() {
+ final boolean result;
+ synchronized (mDataEnabledLock) {
+ result = (mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled
+ && (enabledCount != 0));
+ }
+ if (!result && DBG) log("getAnyDataEnabled " + result);
+ return result;
+ }
+
+ protected boolean isEmergency() {
+ final boolean result;
+ synchronized (mDataEnabledLock) {
+ result = mPhone.isInEcm() || mPhone.isInEmergencyCall();
+ }
+ log("isEmergency: result=" + result);
+ return result;
+ }
+
+ protected int apnTypeToId(String type) {
+ if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DEFAULT)) {
+ return DctConstants.APN_DEFAULT_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_MMS)) {
+ return DctConstants.APN_MMS_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_SUPL)) {
+ return DctConstants.APN_SUPL_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_DUN)) {
+ return DctConstants.APN_DUN_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_HIPRI)) {
+ return DctConstants.APN_HIPRI_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_IMS)) {
+ return DctConstants.APN_IMS_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_FOTA)) {
+ return DctConstants.APN_FOTA_ID;
+ } else if (TextUtils.equals(type, PhoneConstants.APN_TYPE_CBS)) {
+ return DctConstants.APN_CBS_ID;
+ } else {
+ return DctConstants.APN_INVALID_ID;
+ }
+ }
+
+ protected String apnIdToType(int id) {
+ switch (id) {
+ case DctConstants.APN_DEFAULT_ID:
+ return PhoneConstants.APN_TYPE_DEFAULT;
+ case DctConstants.APN_MMS_ID:
+ return PhoneConstants.APN_TYPE_MMS;
+ case DctConstants.APN_SUPL_ID:
+ return PhoneConstants.APN_TYPE_SUPL;
+ case DctConstants.APN_DUN_ID:
+ return PhoneConstants.APN_TYPE_DUN;
+ case DctConstants.APN_HIPRI_ID:
+ return PhoneConstants.APN_TYPE_HIPRI;
+ case DctConstants.APN_IMS_ID:
+ return PhoneConstants.APN_TYPE_IMS;
+ case DctConstants.APN_FOTA_ID:
+ return PhoneConstants.APN_TYPE_FOTA;
+ case DctConstants.APN_CBS_ID:
+ return PhoneConstants.APN_TYPE_CBS;
+ default:
+ log("Unknown id (" + id + ") in apnIdToType");
+ return PhoneConstants.APN_TYPE_DEFAULT;
+ }
+ }
+
+ protected LinkProperties getLinkProperties(String apnType) {
+ int id = apnTypeToId(apnType);
+
+ if (isApnIdEnabled(id)) {
+ // TODO - remove this cdma-only hack and support multiple DCs.
+ DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
+ return dcac.getLinkPropertiesSync();
+ } else {
+ return new LinkProperties();
+ }
+ }
+
+ protected LinkCapabilities getLinkCapabilities(String apnType) {
+ int id = apnTypeToId(apnType);
+ if (isApnIdEnabled(id)) {
+ // TODO - remove this cdma-only hack and support multiple DCs.
+ DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
+ return dcac.getLinkCapabilitiesSync();
+ } else {
+ return new LinkCapabilities();
+ }
+ }
+
+ // tell all active apns of the current condition
+ protected void notifyDataConnection(String reason) {
+ for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) {
+ if (dataEnabled[id]) {
+ mPhone.notifyDataConnection(reason, apnIdToType(id));
+ }
+ }
+ notifyOffApnsOfAvailability(reason);
+ }
+
+ // a new APN has gone active and needs to send events to catch up with the
+ // current condition
+ private void notifyApnIdUpToCurrent(String reason, int apnId) {
+ switch (mState) {
+ case IDLE:
+ case INITING:
+ break;
+ case CONNECTING:
+ case SCANNING:
+ mPhone.notifyDataConnection(reason, apnIdToType(apnId),
+ PhoneConstants.DataState.CONNECTING);
+ break;
+ case CONNECTED:
+ case DISCONNECTING:
+ mPhone.notifyDataConnection(reason, apnIdToType(apnId),
+ PhoneConstants.DataState.CONNECTING);
+ mPhone.notifyDataConnection(reason, apnIdToType(apnId),
+ PhoneConstants.DataState.CONNECTED);
+ break;
+ }
+ }
+
+ // since we normally don't send info to a disconnected APN, we need to do this specially
+ private void notifyApnIdDisconnected(String reason, int apnId) {
+ mPhone.notifyDataConnection(reason, apnIdToType(apnId),
+ PhoneConstants.DataState.DISCONNECTED);
+ }
+
+ // disabled apn's still need avail/unavail notificiations - send them out
+ protected void notifyOffApnsOfAvailability(String reason) {
+ if (DBG) log("notifyOffApnsOfAvailability - reason= " + reason);
+ for (int id = 0; id < DctConstants.APN_NUM_TYPES; id++) {
+ if (!isApnIdEnabled(id)) {
+ notifyApnIdDisconnected(reason, id);
+ }
+ }
+ }
+
+ public boolean isApnTypeEnabled(String apnType) {
+ if (apnType == null) {
+ return false;
+ } else {
+ return isApnIdEnabled(apnTypeToId(apnType));
+ }
+ }
+
+ protected synchronized boolean isApnIdEnabled(int id) {
+ if (id != DctConstants.APN_INVALID_ID) {
+ return dataEnabled[id];
+ }
+ return false;
+ }
+
+ /**
+ * Ensure that we are connected to an APN of the specified type.
+ *
+ * @param type the APN type (currently the only valid values are
+ * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
+ * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
+ * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
+ * broadcast will be sent by the ConnectivityManager when a
+ * connection to the APN has been established.
+ */
+ public synchronized int enableApnType(String type) {
+ int id = apnTypeToId(type);
+ if (id == DctConstants.APN_INVALID_ID) {
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+
+ if (DBG) {
+ log("enableApnType(" + type + "), isApnTypeActive = " + isApnTypeActive(type)
+ + ", isApnIdEnabled =" + isApnIdEnabled(id) + " and state = " + mState);
+ }
+
+ if (!isApnTypeAvailable(type)) {
+ if (DBG) log("type not available");
+ return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
+ }
+
+ if (isApnIdEnabled(id)) {
+ return PhoneConstants.APN_ALREADY_ACTIVE;
+ } else {
+ setEnabled(id, true);
+ }
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+
+ /**
+ * The APN of the specified type is no longer needed. Ensure that if use of
+ * the default APN has not been explicitly disabled, we are connected to the
+ * default APN.
+ *
+ * @param type the APN type. The only valid values are currently
+ * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
+ * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or
+ * {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a
+ * broadcast will be sent by the ConnectivityManager when a
+ * connection to the APN has been disconnected. A {@code
+ * PhoneConstants.APN_REQUEST_FAILED} is returned if the type parameter is
+ * invalid or if the apn wasn't enabled.
+ */
+ public synchronized int disableApnType(String type) {
+ if (DBG) log("disableApnType(" + type + ")");
+ int id = apnTypeToId(type);
+ if (id == DctConstants.APN_INVALID_ID) {
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ if (isApnIdEnabled(id)) {
+ setEnabled(id, false);
+ if (isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) {
+ if (dataEnabled[DctConstants.APN_DEFAULT_ID]) {
+ return PhoneConstants.APN_ALREADY_ACTIVE;
+ } else {
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+ } else {
+ return PhoneConstants.APN_REQUEST_STARTED;
+ }
+ } else {
+ return PhoneConstants.APN_REQUEST_FAILED;
+ }
+ }
+
+ protected void setEnabled(int id, boolean enable) {
+ if (DBG) {
+ log("setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id]
+ + " and enabledCount = " + enabledCount);
+ }
+ Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
+ msg.arg1 = id;
+ msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
+ sendMessage(msg);
+ }
+
+ protected void onEnableApn(int apnId, int enabled) {
+ if (DBG) {
+ log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
+ ", enabled=" + enabled + ", dataEnabled = " + dataEnabled[apnId] +
+ ", enabledCount = " + enabledCount + ", isApnTypeActive = " +
+ isApnTypeActive(apnIdToType(apnId)));
+ }
+ if (enabled == DctConstants.ENABLED) {
+ synchronized (this) {
+ if (!dataEnabled[apnId]) {
+ dataEnabled[apnId] = true;
+ enabledCount++;
+ }
+ }
+ 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 (dataEnabled[apnId]) {
+ dataEnabled[apnId] = false;
+ enabledCount--;
+ didDisable = true;
+ }
+ }
+ if (didDisable) {
+ if ((enabledCount == 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 (dataEnabled[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();
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when we switch APNs.
+ *
+ * mRequestedApnType is set prior to call
+ * To be overridden.
+ */
+ protected void onEnableNewApn() {
+ }
+
+ /**
+ * Called when EVENT_RESET_DONE is received so goto
+ * IDLE state and send notifications to those interested.
+ *
+ * TODO - currently unused. Needs to be hooked into DataConnection cleanup
+ * TODO - needs to pass some notion of which connection is reset..
+ */
+ protected void onResetDone(AsyncResult ar) {
+ if (DBG) log("EVENT_RESET_DONE");
+ String reason = null;
+ if (ar.userObj instanceof String) {
+ reason = (String) ar.userObj;
+ }
+ gotoIdleAndNotifyDataConnection(reason);
+ }
+
+ /**
+ * Prevent mobile data connections from being established, or once again
+ * allow mobile data connections. If the state toggles, then either tear
+ * down or set up data, as appropriate to match the new state.
+ *
+ * @param enable indicates whether to enable ({@code true}) or disable (
+ * {@code false}) data
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean setInternalDataEnabled(boolean enable) {
+ if (DBG)
+ log("setInternalDataEnabled(" + enable + ")");
+
+ Message msg = obtainMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE);
+ msg.arg1 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
+ sendMessage(msg);
+ return true;
+ }
+
+ protected void onSetInternalDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ mInternalDataEnabled = enabled;
+ if (enabled) {
+ log("onSetInternalDataEnabled: changed to enabled, try to setup data call");
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ log("onSetInternalDataEnabled: changed to disabled, cleanUpAllConnections");
+ cleanUpAllConnections(null);
+ }
+ }
+ }
+
+ public void cleanUpAllConnections(String cause) {
+ Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
+ msg.obj = cause;
+ sendMessage(msg);
+ }
+
+ public abstract boolean isDisconnected();
+
+ protected void onSetUserDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mUserDataEnabled != enabled) {
+ mUserDataEnabled = enabled;
+ Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+ Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
+ if (getDataOnRoamingEnabled() == false &&
+ mPhone.getServiceState().getRoaming() == true) {
+ if (enabled) {
+ notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
+ } else {
+ notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
+ }
+ }
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
+ }
+ }
+ }
+ }
+
+ protected void onSetDependencyMet(String apnType, boolean met) {
+ }
+
+ protected void onSetPolicyDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (sPolicyDataEnabled != enabled) {
+ sPolicyDataEnabled = enabled;
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
+ }
+ }
+ }
+ }
+
+ protected String getReryConfig(boolean forDefault) {
+ int nt = mPhone.getServiceState().getNetworkType();
+
+ if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) ||
+ (nt == TelephonyManager.NETWORK_TYPE_1xRTT) ||
+ (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) ||
+ (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
+ (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) ||
+ (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) {
+ // CDMA variant
+ return SystemProperties.get("ro.cdma.data_retry_config");
+ } else {
+ // Use GSM varient for all others.
+ if (forDefault) {
+ return SystemProperties.get("ro.gsm.data_retry_config");
+ } else {
+ return SystemProperties.get("ro.gsm.2nd_data_retry_config");
+ }
+ }
+ }
+
+ protected void resetAllRetryCounts() {
+ for (ApnContext ac : mApnContexts.values()) {
+ ac.setRetryCount(0);
+ }
+ for (DataConnection dc : mDataConnections.values()) {
+ dc.resetRetryCount();
+ }
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("DataConnectionTracker:");
+ pw.println(" mInternalDataEnabled=" + mInternalDataEnabled);
+ pw.println(" mUserDataEnabled=" + mUserDataEnabled);
+ pw.println(" sPolicyDataEnabed=" + sPolicyDataEnabled);
+ pw.println(" dataEnabled:");
+ for(int i=0; i < dataEnabled.length; i++) {
+ pw.printf(" dataEnabled[%d]=%b\n", i, dataEnabled[i]);
+ }
+ pw.flush();
+ pw.println(" enabledCount=" + enabledCount);
+ pw.println(" mRequestedApnType=" + mRequestedApnType);
+ pw.println(" mPhone=" + mPhone.getPhoneName());
+ pw.println(" mActivity=" + mActivity);
+ pw.println(" mState=" + mState);
+ pw.println(" mTxPkts=" + mTxPkts);
+ pw.println(" mRxPkts=" + mRxPkts);
+ pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
+ pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
+ pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
+ pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
+ pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
+ pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
+ pw.println(" mIsWifiConnected=" + mIsWifiConnected);
+ pw.println(" mReconnectIntent=" + mReconnectIntent);
+ pw.println(" mCidActive=" + mCidActive);
+ pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation);
+ pw.println(" mIsScreenOn=" + mIsScreenOn);
+ pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
+ pw.flush();
+ pw.println(" ***************************************");
+ Set > mDcSet = mDataConnections.entrySet();
+ pw.println(" mDataConnections: count=" + mDcSet.size());
+ for (Entry entry : mDcSet) {
+ pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
+ entry.getValue().dump(fd, pw, args);
+ }
+ pw.println(" ***************************************");
+ pw.flush();
+ Set> mApnToDcIdSet = mApnToDataConnectionId.entrySet();
+ pw.println(" mApnToDataConnectonId size=" + mApnToDcIdSet.size());
+ for (Entry entry : mApnToDcIdSet) {
+ pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
+ }
+ pw.println(" ***************************************");
+ pw.flush();
+ if (mApnContexts != null) {
+ Set> mApnContextsSet = mApnContexts.entrySet();
+ pw.println(" mApnContexts size=" + mApnContextsSet.size());
+ for (Entry entry : mApnContextsSet) {
+ entry.getValue().dump(fd, pw, args);
+ }
+ pw.println(" ***************************************");
+ } else {
+ pw.println(" mApnContexts=null");
+ }
+ pw.flush();
+ pw.println(" mActiveApn=" + mActiveApn);
+ if (mAllApns != null) {
+ pw.println(" mAllApns size=" + mAllApns.size());
+ for (int i=0; i < mAllApns.size(); i++) {
+ pw.printf(" mAllApns[%d]: %s\n", i, mAllApns.get(i));
+ }
+ pw.flush();
+ } else {
+ pw.println(" mAllApns=null");
+ }
+ pw.println(" mPreferredApn=" + mPreferredApn);
+ pw.println(" mIsPsRestricted=" + mIsPsRestricted);
+ pw.println(" mIsDisposed=" + mIsDisposed);
+ pw.println(" mIntentReceiver=" + mIntentReceiver);
+ pw.println(" mDataRoamingSettingObserver=" + mDataRoamingSettingObserver);
+ pw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DebugService.java b/src/java/com/android/internal/telephony/DebugService.java
new file mode 100644
index 0000000000000000000000000000000000000000..29fea6e83fcde635c950de25e4d90da99a2cf504
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DebugService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A debug service that will dump telephony's state
+ *
+ * Currently this "Service" has a proxy in the phone app
+ * com.android.phone.TelephonyDebugService which actually
+ * invokes the dump method.
+ */
+public class DebugService {
+ private static String TAG = "DebugService";
+
+ /** Constructor */
+ public DebugService() {
+ log("DebugService:");
+ }
+
+ /**
+ * Dump the state of various objects, add calls to other objects as desired.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ log("dump: +");
+ PhoneProxy phoneProxy = null;
+ PhoneBase phoneBase = null;
+
+ try {
+ phoneProxy = (PhoneProxy) PhoneFactory.getDefaultPhone();
+ } catch (Exception e) {
+ pw.println("Telephony DebugService: Could not getDefaultPhone e=" + e);
+ return;
+ }
+ try {
+ phoneBase = (PhoneBase)phoneProxy.getActivePhone();
+ } catch (Exception e) {
+ pw.println("Telephony DebugService: Could not PhoneBase e=" + e);
+ return;
+ }
+
+ /**
+ * Surround each of the sub dump's with try/catch so even
+ * if one fails we'll be able to dump the next ones.
+ */
+ pw.println();
+ pw.println("++++++++++++++++++++++++++++++++");
+ pw.flush();
+ try {
+ phoneBase.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.mDataConnectionTracker.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.getServiceStateTracker().dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.getCallTracker().dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ ((RIL)phoneBase.mCM).dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ log("dump: -");
+ }
+
+ private static void log(String s) {
+ Log.d(TAG, "DebugService " + s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d16443cbebe02b61668a01c7a158ddae182366d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -0,0 +1,281 @@
+/*
+ * 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.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.CellInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephonyRegistry;
+
+/**
+ * broadcast intents
+ */
+public class DefaultPhoneNotifier implements PhoneNotifier {
+
+ static final String LOG_TAG = "GSM";
+ private static final boolean DBG = true;
+ private ITelephonyRegistry mRegistry;
+
+ /*package*/
+ DefaultPhoneNotifier() {
+ mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ }
+
+ public void notifyPhoneState(Phone sender) {
+ Call ringingCall = sender.getRingingCall();
+ String incomingNumber = "";
+ if (ringingCall != null && ringingCall.getEarliestConnection() != null){
+ incomingNumber = ringingCall.getEarliestConnection().getAddress();
+ }
+ try {
+ mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyServiceState(Phone sender) {
+ ServiceState ss = sender.getServiceState();
+ if (ss == null) {
+ ss = new ServiceState();
+ ss.setStateOutOfService();
+ }
+ try {
+ mRegistry.notifyServiceState(ss);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifySignalStrength(Phone sender) {
+ try {
+ mRegistry.notifySignalStrength(sender.getSignalStrength());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyMessageWaitingChanged(Phone sender) {
+ try {
+ mRegistry.notifyMessageWaitingChanged(sender.getMessageWaitingIndicator());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyCallForwardingChanged(Phone sender) {
+ try {
+ mRegistry.notifyCallForwardingChanged(sender.getCallForwardingIndicator());
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataActivity(Phone sender) {
+ try {
+ mRegistry.notifyDataActivity(convertDataActivityState(sender.getDataActivityState()));
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataConnection(Phone sender, String reason, String apnType,
+ PhoneConstants.DataState state) {
+ doNotifyDataConnection(sender, reason, apnType, state);
+ }
+
+ private void doNotifyDataConnection(Phone sender, String reason, String apnType,
+ PhoneConstants.DataState state) {
+ // TODO
+ // use apnType as the key to which connection we're talking about.
+ // pass apnType back up to fetch particular for this one.
+ TelephonyManager telephony = TelephonyManager.getDefault();
+ LinkProperties linkProperties = null;
+ LinkCapabilities linkCapabilities = null;
+ boolean roaming = false;
+
+ if (state == PhoneConstants.DataState.CONNECTED) {
+ linkProperties = sender.getLinkProperties(apnType);
+ linkCapabilities = sender.getLinkCapabilities(apnType);
+ }
+ ServiceState ss = sender.getServiceState();
+ if (ss != null) roaming = ss.getRoaming();
+
+ try {
+ mRegistry.notifyDataConnection(
+ convertDataState(state),
+ sender.isDataConnectivityPossible(apnType), reason,
+ sender.getActiveApnHost(apnType),
+ apnType,
+ linkProperties,
+ linkCapabilities,
+ ((telephony!=null) ? telephony.getNetworkType() :
+ TelephonyManager.NETWORK_TYPE_UNKNOWN),
+ roaming);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
+ try {
+ mRegistry.notifyDataConnectionFailed(reason, apnType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyCellLocation(Phone sender) {
+ Bundle data = new Bundle();
+ sender.getCellLocation().fillInNotifierBundle(data);
+ try {
+ mRegistry.notifyCellLocation(data);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ public void notifyCellInfo(Phone sender, CellInfo cellInfo) {
+ try {
+ mRegistry.notifyCellInfo(cellInfo);
+ } catch (RemoteException ex) {
+
+ }
+ }
+
+ public void notifyOtaspChanged(Phone sender, int otaspMode) {
+ try {
+ mRegistry.notifyOtaspChanged(otaspMode);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ private void log(String s) {
+ Log.d(LOG_TAG, "[PhoneNotifier] " + s);
+ }
+
+ /**
+ * Convert the {@link State} enum into the TelephonyManager.CALL_STATE_* constants
+ * for the public API.
+ */
+ public static int convertCallState(PhoneConstants.State state) {
+ switch (state) {
+ case RINGING:
+ return TelephonyManager.CALL_STATE_RINGING;
+ case OFFHOOK:
+ return TelephonyManager.CALL_STATE_OFFHOOK;
+ default:
+ return TelephonyManager.CALL_STATE_IDLE;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.CALL_STATE_* constants into the {@link State} enum
+ * for the public API.
+ */
+ public static PhoneConstants.State convertCallState(int state) {
+ switch (state) {
+ case TelephonyManager.CALL_STATE_RINGING:
+ return PhoneConstants.State.RINGING;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ return PhoneConstants.State.OFFHOOK;
+ default:
+ return PhoneConstants.State.IDLE;
+ }
+ }
+
+ /**
+ * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants
+ * for the public API.
+ */
+ public static int convertDataState(PhoneConstants.DataState state) {
+ switch (state) {
+ case CONNECTING:
+ return TelephonyManager.DATA_CONNECTING;
+ case CONNECTED:
+ return TelephonyManager.DATA_CONNECTED;
+ case SUSPENDED:
+ return TelephonyManager.DATA_SUSPENDED;
+ default:
+ return TelephonyManager.DATA_DISCONNECTED;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.DATA_* constants into {@link DataState} enum
+ * for the public API.
+ */
+ public static PhoneConstants.DataState convertDataState(int state) {
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTING:
+ return PhoneConstants.DataState.CONNECTING;
+ case TelephonyManager.DATA_CONNECTED:
+ return PhoneConstants.DataState.CONNECTED;
+ case TelephonyManager.DATA_SUSPENDED:
+ return PhoneConstants.DataState.SUSPENDED;
+ default:
+ return PhoneConstants.DataState.DISCONNECTED;
+ }
+ }
+
+ /**
+ * Convert the {@link DataState} enum into the TelephonyManager.DATA_* constants
+ * for the public API.
+ */
+ public static int convertDataActivityState(Phone.DataActivityState state) {
+ switch (state) {
+ case DATAIN:
+ return TelephonyManager.DATA_ACTIVITY_IN;
+ case DATAOUT:
+ return TelephonyManager.DATA_ACTIVITY_OUT;
+ case DATAINANDOUT:
+ return TelephonyManager.DATA_ACTIVITY_INOUT;
+ case DORMANT:
+ return TelephonyManager.DATA_ACTIVITY_DORMANT;
+ default:
+ return TelephonyManager.DATA_ACTIVITY_NONE;
+ }
+ }
+
+ /**
+ * Convert the TelephonyManager.DATA_* constants into the {@link DataState} enum
+ * for the public API.
+ */
+ public static Phone.DataActivityState convertDataActivityState(int state) {
+ switch (state) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ return Phone.DataActivityState.DATAIN;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ return Phone.DataActivityState.DATAOUT;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ return Phone.DataActivityState.DATAINANDOUT;
+ case TelephonyManager.DATA_ACTIVITY_DORMANT:
+ return Phone.DataActivityState.DORMANT;
+ default:
+ return Phone.DataActivityState.NONE;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/DriverCall.java b/src/java/com/android/internal/telephony/DriverCall.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1e63aef24c3848fc957d47b3d380a3a597d48d7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DriverCall.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+//import com.android.internal.telephony.*;
+import android.util.Log;
+import java.lang.Comparable;
+import android.telephony.PhoneNumberUtils;
+
+/**
+ * {@hide}
+ */
+public class DriverCall implements Comparable {
+ static final String LOG_TAG = "RILB";
+
+ public enum State {
+ ACTIVE,
+ HOLDING,
+ DIALING, // MO call only
+ ALERTING, // MO call only
+ INCOMING, // MT call only
+ WAITING; // MT call only
+ // If you add a state, make sure to look for the switch()
+ // statements that use this enum
+ }
+
+ public int index;
+ public boolean isMT;
+ public State state; // May be null if unavail
+ public boolean isMpty;
+ public String number;
+ public int TOA;
+ public boolean isVoice;
+ public boolean isVoicePrivacy;
+ public int als;
+ public int numberPresentation;
+ public String name;
+ public int namePresentation;
+ public UUSInfo uusInfo;
+
+ /** returns null on error */
+ static DriverCall
+ fromCLCCLine(String line) {
+ DriverCall ret = new DriverCall();
+
+ //+CLCC: 1,0,2,0,0,\"+18005551212\",145
+ // index,isMT,state,mode,isMpty(,number,TOA)?
+ ATResponseParser p = new ATResponseParser(line);
+
+ try {
+ ret.index = p.nextInt();
+ ret.isMT = p.nextBoolean();
+ ret.state = stateFromCLCC(p.nextInt());
+
+ ret.isVoice = (0 == p.nextInt());
+ ret.isMpty = p.nextBoolean();
+
+ // use ALLOWED as default presentation while parsing CLCC
+ ret.numberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
+
+ if (p.hasMore()) {
+ // Some lame implementations return strings
+ // like "NOT AVAILABLE" in the CLCC line
+ ret.number = PhoneNumberUtils.extractNetworkPortionAlt(p.nextString());
+
+ if (ret.number.length() == 0) {
+ ret.number = null;
+ }
+
+ ret.TOA = p.nextInt();
+
+ // Make sure there's a leading + on addresses with a TOA
+ // of 145
+
+ ret.number = PhoneNumberUtils.stringFromStringAndTOA(
+ ret.number, ret.TOA);
+
+ }
+ } catch (ATParseEx ex) {
+ Log.e(LOG_TAG,"Invalid CLCC line: '" + line + "'");
+ return null;
+ }
+
+ return ret;
+ }
+
+ public
+ DriverCall() {
+ }
+
+ public String
+ toString() {
+ return "id=" + index + ","
+ + state + ","
+ + "toa=" + TOA + ","
+ + (isMpty ? "conf" : "norm") + ","
+ + (isMT ? "mt" : "mo") + ","
+ + als + ","
+ + (isVoice ? "voc" : "nonvoc") + ","
+ + (isVoicePrivacy ? "evp" : "noevp") + ","
+ /*+ "number=" + number */ + ",cli=" + numberPresentation + ","
+ /*+ "name="+ name */ + "," + namePresentation;
+ }
+
+ public static State
+ stateFromCLCC(int state) throws ATParseEx {
+ switch(state) {
+ case 0: return State.ACTIVE;
+ case 1: return State.HOLDING;
+ case 2: return State.DIALING;
+ case 3: return State.ALERTING;
+ case 4: return State.INCOMING;
+ case 5: return State.WAITING;
+ default:
+ throw new ATParseEx("illegal call state " + state);
+ }
+ }
+
+ public static int
+ presentationFromCLIP(int cli) throws ATParseEx
+ {
+ switch(cli) {
+ case 0: return PhoneConstants.PRESENTATION_ALLOWED;
+ case 1: return PhoneConstants.PRESENTATION_RESTRICTED;
+ case 2: return PhoneConstants.PRESENTATION_UNKNOWN;
+ case 3: return PhoneConstants.PRESENTATION_PAYPHONE;
+ default:
+ throw new ATParseEx("illegal presentation " + cli);
+ }
+ }
+
+ //***** Comparable Implementation
+
+ /** For sorting by index */
+ public int
+ compareTo (Object o) {
+ DriverCall dc;
+
+ dc = (DriverCall)o;
+
+ if (index < dc.index) {
+ return -1;
+ } else if (index == dc.index) {
+ return 0;
+ } else { /*index > dc.index*/
+ return 1;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/EventLogTags.logtags b/src/java/com/android/internal/telephony/EventLogTags.logtags
new file mode 100644
index 0000000000000000000000000000000000000000..427e5dafa73224cc095a72b2d5609b43c84ae0c6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/EventLogTags.logtags
@@ -0,0 +1,73 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.telephony;
+
+# PDP Context has a bad DNS address
+50100 pdp_bad_dns_address (dns_address|3)
+
+# For data connection on PDP context, reached the data-out-without-data-in
+# packet count that triggers a countdown to radio restart
+50101 pdp_radio_reset_countdown_triggered (out_packet_count|1|1)
+
+# Radio restart - timed out with no incoming packets.
+50102 pdp_radio_reset (out_packet_count|1|1)
+
+# PDP context reset - timed out with no incoming packets.
+50103 pdp_context_reset (out_packet_count|1|1)
+
+# Reregister to data network - timed out with no incoming packets.
+50104 pdp_reregister_network (out_packet_count|1|1)
+
+# PDP Setup failures
+50105 pdp_setup_fail (cause|1|5), (cid|1|5), (network_type|1|5)
+
+# Call drops
+50106 call_drop (cause|1|5), (cid|1|5), (network_type|1|5)
+
+# Data network registration failed after successful voice registration
+50107 data_network_registration_fail (op_numeric|1|5), (cid|1|5)
+
+# Suspicious status of data connection while radio poweroff
+50108 data_network_status_on_radio_off (dc_state|3), (enable|1|5)
+
+# PDP drop caused by network
+50109 pdp_network_drop (cid|1|5), (network_type|1|5)
+
+# CDMA data network setup failure
+50110 cdma_data_setup_failed (cause|1|5), (cid|1|5), (network_type|1|5)
+
+# CDMA data network drop
+50111 cdma_data_drop (cid|1|5), (network_type|1|5)
+
+# GSM radio access technology switched
+50112 gsm_rat_switched (cid|1|5), (network_from|1|5), (network_to|1|5)
+
+# GSM data connection state transition
+50113 gsm_data_state_change (oldState|3), (newState|3)
+
+# GSM service state transition
+50114 gsm_service_state_change (oldState|1|5), (oldGprsState|1|5), (newState|1|5), (newGprsState|1|5)
+
+# CDMA data connection state transition
+50115 cdma_data_state_change (oldState|3), (newState|3)
+
+# CDMA service state transition
+50116 cdma_service_state_change (oldState|1|5), (oldDataState|1|5), (newState|1|5), (newDataState|1|5)
+
+# Bad IP address
+50117 bad_ip_address (ip_address|3)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_GET_DATA_CALL_LIST
+50118 data_stall_recovery_get_data_call_list (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_CLEANUP
+50119 data_stall_recovery_cleanup (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_REREGISTER
+50120 data_stall_recovery_reregister (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_RADIO_RESTART
+50121 data_stall_recovery_radio_restart (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP
+50122 data_stall_recovery_radio_restart_with_prop (out_packet_count|1|1)
diff --git a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..f700dfefcb2a0f25beaaa6e81f38aa732245e132
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
@@ -0,0 +1,101 @@
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.AdnRecord;
+
+
+
+/** Interface for applications to access the ICC phone book.
+ *
+ * The following code snippet demonstrates a static method to
+ * retrieve the IIccPhoneBook interface from Android:
+ * private static IIccPhoneBook getSimPhoneBookInterface()
+ throws DeadObjectException {
+ IServiceManager sm = ServiceManagerNative.getDefault();
+ IIccPhoneBook spb;
+ spb = IIccPhoneBook.Stub.asInterface(sm.getService("iccphonebook"));
+ return spb;
+}
+ *
+ */
+
+interface IIccPhoneBook {
+
+ /**
+ * Loads the AdnRecords in efid and returns them as a
+ * List of AdnRecords
+ *
+ * @param efid the EF id of a ADN-like SIM
+ * @return List of AdnRecord
+ */
+ List getAdnRecordsInEf(int efid);
+
+ /**
+ * Replace oldAdn with newAdn in ADN-like record in EF
+ *
+ * getAdnRecordsInEf must be called at least once before this function,
+ * otherwise an error will be returned
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param oldTag adn tag to be replaced
+ * @param oldPhoneNumber adn number to be replaced
+ * Set both oldTag and oldPhoneNubmer to "" means to replace an
+ * empty record, aka, insert new record
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number ot be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @return true for success
+ */
+ boolean updateAdnRecordsInEfBySearch(int efid,
+ String oldTag, String oldPhoneNumber,
+ String newTag, String newPhoneNumber,
+ String pin2);
+
+ /**
+ * Update an ADN-like EF record by record index
+ *
+ * This is useful for iteration the whole ADN file, such as write the whole
+ * phone book or erase/format the whole phonebook
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number to be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param index is 1-based adn record index to be updated
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @return true for success
+ */
+ boolean updateAdnRecordsInEfByIndex(int efid, String newTag,
+ String newPhoneNumber, int index,
+ String pin2);
+
+ /**
+ * Get the max munber of records in efid
+ *
+ * @param efid the EF id of a ADN-like SIM
+ * @return int[3] array
+ * recordSizes[0] is the single record length
+ * recordSizes[1] is the total length of the EF file
+ * recordSizes[2] is the number of records in the EF file
+ */
+ int[] getAdnRecordsSize(int efid);
+
+}
diff --git a/src/java/com/android/internal/telephony/ISms.aidl b/src/java/com/android/internal/telephony/ISms.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..735f986917bc1ba00fc9853899eaef8a15c2f233
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ISms.aidl
@@ -0,0 +1,201 @@
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.telephony;
+
+import android.app.PendingIntent;
+import com.android.internal.telephony.SmsRawData;
+
+/** Interface for applications to access the ICC phone book.
+ *
+ * The following code snippet demonstrates a static method to
+ * retrieve the ISms interface from Android:
+ * private static ISms getSmsInterface()
+ throws DeadObjectException {
+ IServiceManager sm = ServiceManagerNative.getDefault();
+ ISms ss;
+ ss = ISms.Stub.asInterface(sm.getService("isms"));
+ return ss;
+}
+ *
+ */
+
+interface ISms {
+ /**
+ * Retrieves all messages currently stored on ICC.
+ *
+ * @return list of SmsRawData of all sms on ICC
+ */
+ List getAllMessagesFromIccEf();
+
+ /**
+ * Update the specified message on the ICC.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_ICC_READ,
+ * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+ * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+ * @param pdu the raw PDU to store
+ * @return success or not
+ *
+ */
+ boolean updateMessageOnIccEf(int messageIndex, int newStatus,
+ in byte[] pdu);
+
+ /**
+ * Copy a raw SMS PDU to the ICC.
+ *
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
+ * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
+ * @return success or not
+ *
+ */
+ boolean copyMessageToIccEf(int status, in byte[] pdu, in byte[] smsc);
+
+ /**
+ * Send a data SMS.
+ *
+ * @param smsc the SMSC to send the message through, or NULL for the
+ * default SMSC
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ void sendData(in String destAddr, in String scAddr, in int destPort,
+ in byte[] data, in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+
+ /**
+ * Send an SMS.
+ *
+ * @param smsc the SMSC to send the message through, or NULL for the
+ * default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ void sendText(in String destAddr, in String scAddr, in String text,
+ in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+
+ /**
+ * Send a multi-part text based SMS.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
.
+ * @param deliveryIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ */
+ void sendMultipartText(in String destinationAddress, in String scAddress,
+ in List parts, in List sentIntents,
+ in List deliveryIntents);
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #disableCellBroadcast(int)
+ */
+ boolean enableCellBroadcast(int messageIdentifier);
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int)
+ */
+ boolean disableCellBroadcast(int messageIdentifier);
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable
+ * a message identifier range, they must both disable it for the device
+ * to stop receiving those messages.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #disableCellBroadcastRange(int, int)
+ */
+ boolean enableCellBroadcastRange(int startMessageId, int endMessageId);
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable
+ * a message identifier range, they must both disable it for the device
+ * to stop receiving those messages.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int)
+ */
+ boolean disableCellBroadcastRange(int startMessageId, int endMessageId);
+
+}
diff --git a/src/java/com/android/internal/telephony/IccCard.java b/src/java/com/android/internal/telephony/IccCard.java
new file mode 100644
index 0000000000000000000000000000000000000000..740292c3ec55a7e76c3a6fb63615986448959cda
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccCard.java
@@ -0,0 +1,958 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.Manifest.permission.READ_PHONE_STATE;
+import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.gsm.SIMFileHandler;
+import com.android.internal.telephony.gsm.SIMRecords;
+import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
+import com.android.internal.telephony.cdma.CdmaLteUiccFileHandler;
+import com.android.internal.telephony.cdma.CdmaLteUiccRecords;
+import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.cdma.RuimFileHandler;
+import com.android.internal.telephony.cdma.RuimRecords;
+
+import com.android.internal.R;
+
+/**
+ * {@hide}
+ */
+public class IccCard {
+ protected String mLogTag;
+ protected boolean mDbg;
+
+ protected IccCardStatus mIccCardStatus = null;
+ protected IccCardConstants.State mState = null;
+ private final Object mStateMonitor = new Object();
+
+ protected boolean is3gpp = true;
+ protected boolean isSubscriptionFromIccCard = true;
+ protected CdmaSubscriptionSourceManager mCdmaSSM = null;
+ protected PhoneBase mPhone;
+ private IccRecords mIccRecords;
+ private IccFileHandler mIccFileHandler;
+ private CatService mCatService;
+
+ private RegistrantList mAbsentRegistrants = new RegistrantList();
+ private RegistrantList mPinLockedRegistrants = new RegistrantList();
+ private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
+ protected RegistrantList mReadyRegistrants = new RegistrantList();
+ protected RegistrantList mRuimReadyRegistrants = new RegistrantList();
+
+ private boolean mDesiredPinLocked;
+ private boolean mDesiredFdnEnabled;
+ private boolean mIccPinLocked = true; // Default to locked
+ private boolean mIccFdnEnabled = false; // Default to disabled.
+ // Will be updated when SIM_READY.
+
+ /* Parameter is3gpp's values to be passed to constructor */
+ public final static boolean CARD_IS_3GPP = true;
+ public final static boolean CARD_IS_NOT_3GPP = false;
+
+ protected static final int EVENT_ICC_LOCKED = 1;
+ private static final int EVENT_GET_ICC_STATUS_DONE = 2;
+ protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
+ private static final int EVENT_PINPUK_DONE = 4;
+ private static final int EVENT_REPOLL_STATUS_DONE = 5;
+ protected static final int EVENT_ICC_READY = 6;
+ private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
+ private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
+ private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
+ private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
+ private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
+ private static final int EVENT_ICC_STATUS_CHANGED = 12;
+ private static final int EVENT_CARD_REMOVED = 13;
+ private static final int EVENT_CARD_ADDED = 14;
+ protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 15;
+ protected static final int EVENT_RADIO_ON = 16;
+
+ public IccCardConstants.State getState() {
+ if (mState == null) {
+ switch(mPhone.mCM.getRadioState()) {
+ /* This switch block must not return anything in
+ * IccCardConstants.State.isLocked() or IccCardConstants.State.ABSENT.
+ * If it does, handleSimStatus() may break
+ */
+ case RADIO_OFF:
+ case RADIO_UNAVAILABLE:
+ return IccCardConstants.State.UNKNOWN;
+ default:
+ if (!is3gpp && !isSubscriptionFromIccCard) {
+ // CDMA can get subscription from NV. In that case,
+ // subscription is ready as soon as Radio is ON.
+ return IccCardConstants.State.READY;
+ }
+ }
+ } else {
+ return mState;
+ }
+
+ return IccCardConstants.State.UNKNOWN;
+ }
+
+ public IccCard(PhoneBase phone, String logTag, Boolean is3gpp, Boolean dbg) {
+ mLogTag = logTag;
+ mDbg = dbg;
+ if (mDbg) log("[IccCard] Creating card type " + (is3gpp ? "3gpp" : "3gpp2"));
+ mPhone = phone;
+ this.is3gpp = is3gpp;
+ mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(mPhone.getContext(),
+ mPhone.mCM, mHandler, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
+ if (phone.mCM.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE
+ && phone instanceof CDMALTEPhone) {
+ mIccFileHandler = new CdmaLteUiccFileHandler(this, "", mPhone.mCM);
+ mIccRecords = new CdmaLteUiccRecords(this, mPhone.mContext, mPhone.mCM);
+ } else {
+ // Correct aid will be set later (when GET_SIM_STATUS returns)
+ mIccFileHandler = is3gpp ? new SIMFileHandler(this, "", mPhone.mCM) :
+ new RuimFileHandler(this, "", mPhone.mCM);
+ mIccRecords = is3gpp ? new SIMRecords(this, mPhone.mContext, mPhone.mCM) :
+ new RuimRecords(this, mPhone.mContext, mPhone.mCM);
+ }
+ mCatService = CatService.getInstance(mPhone.mCM, mIccRecords,
+ mPhone.mContext, mIccFileHandler, this);
+ mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mPhone.mCM.registerForOn(mHandler, EVENT_RADIO_ON, null);
+ mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_STATUS_CHANGED, null);
+ }
+
+ public void dispose() {
+ if (mDbg) log("[IccCard] Disposing card type " + (is3gpp ? "3gpp" : "3gpp2"));
+ mPhone.mCM.unregisterForIccStatusChanged(mHandler);
+ mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
+ mPhone.mCM.unregisterForOn(mHandler);
+ mCatService.dispose();
+ mCdmaSSM.dispose(mHandler);
+ mIccRecords.dispose();
+ mIccFileHandler.dispose();
+ }
+
+ protected void finalize() {
+ if (mDbg) log("[IccCard] Finalized card type " + (is3gpp ? "3gpp" : "3gpp2"));
+ }
+
+ public IccRecords getIccRecords() {
+ return mIccRecords;
+ }
+
+ public IccFileHandler getIccFileHandler() {
+ return mIccFileHandler;
+ }
+
+ /**
+ * Notifies handler of any transition into IccCardConstants.State.ABSENT
+ */
+ public void registerForAbsent(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mAbsentRegistrants.add(r);
+
+ if (getState() == IccCardConstants.State.ABSENT) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForAbsent(Handler h) {
+ mAbsentRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies handler of any transition into IccCardConstants.State.NETWORK_LOCKED
+ */
+ public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mNetworkLockedRegistrants.add(r);
+
+ if (getState() == IccCardConstants.State.NETWORK_LOCKED) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForNetworkLocked(Handler h) {
+ mNetworkLockedRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies handler of any transition into IccCardConstants.State.isPinLocked()
+ */
+ public void registerForLocked(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mPinLockedRegistrants.add(r);
+
+ if (getState().isPinLocked()) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForLocked(Handler h) {
+ mPinLockedRegistrants.remove(h);
+ }
+
+ public void registerForReady(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mReadyRegistrants.add(r);
+
+ if (getState() == IccCardConstants.State.READY) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+
+ public void unregisterForReady(Handler h) {
+ synchronized (mStateMonitor) {
+ mReadyRegistrants.remove(h);
+ }
+ }
+
+ public IccCardConstants.State getRuimState() {
+ if(mIccCardStatus != null) {
+ return getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
+ } else {
+ return IccCardConstants.State.UNKNOWN;
+ }
+ }
+
+ public void registerForRuimReady(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ synchronized (mStateMonitor) {
+ mRuimReadyRegistrants.add(r);
+
+ if (getState() == IccCardConstants.State.READY &&
+ getRuimState() == IccCardConstants.State.READY ) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ }
+
+ public void unregisterForRuimReady(Handler h) {
+ synchronized (mStateMonitor) {
+ mRuimReadyRegistrants.remove(h);
+ }
+ }
+
+ /**
+ * Supply the ICC PIN to the ICC
+ *
+ * When the operation is complete, onComplete will be sent to its
+ * Handler.
+ *
+ * onComplete.obj will be an AsyncResult
+ *
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ *
+ * If the supplied PIN is incorrect:
+ * ((AsyncResult)onComplete.obj).exception != null
+ * && ((AsyncResult)onComplete.obj).exception
+ * instanceof com.android.internal.telephony.gsm.CommandException)
+ * && ((CommandException)(((AsyncResult)onComplete.obj).exception))
+ * .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
+ *
+ *
+ */
+
+ public void supplyPin (String pin, Message onComplete) {
+ mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPuk (String puk, String newPin, Message onComplete) {
+ mPhone.mCM.supplyIccPuk(puk, newPin,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPin2 (String pin2, Message onComplete) {
+ mPhone.mCM.supplyIccPin2(pin2,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
+ mPhone.mCM.supplyIccPuk2(puk2, newPin2,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyNetworkDepersonalization (String pin, Message onComplete) {
+ mPhone.mCM.supplyNetworkDepersonalization(pin,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ /**
+ * Check whether ICC pin lock is enabled
+ * This is a sync call which returns the cached pin enabled state
+ *
+ * @return true for ICC locked enabled
+ * false for ICC locked disabled
+ */
+ public boolean getIccLockEnabled() {
+ return mIccPinLocked;
+ }
+
+ /**
+ * Check whether ICC fdn (fixed dialing number) is enabled
+ * This is a sync call which returns the cached pin enabled state
+ *
+ * @return true for ICC fdn enabled
+ * false for ICC fdn disabled
+ */
+ public boolean getIccFdnEnabled() {
+ return mIccFdnEnabled;
+ }
+
+ /**
+ * Set the ICC pin lock enabled or disabled
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param enabled "true" for locked "false" for unlocked.
+ * @param password needed to change the ICC pin state, aka. Pin1
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void setIccLockEnabled (boolean enabled,
+ String password, Message onComplete) {
+ int serviceClassX;
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX;
+
+ mDesiredPinLocked = enabled;
+
+ mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
+ enabled, password, serviceClassX,
+ mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
+ }
+
+ /**
+ * Set the ICC fdn enabled or disabled
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param enabled "true" for locked "false" for unlocked.
+ * @param password needed to change the ICC fdn enable, aka Pin2
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void setIccFdnEnabled (boolean enabled,
+ String password, Message onComplete) {
+ int serviceClassX;
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX +
+ CommandsInterface.SERVICE_CLASS_SMS;
+
+ mDesiredFdnEnabled = enabled;
+
+ mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
+ enabled, password, serviceClassX,
+ mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
+ }
+
+ /**
+ * Change the ICC password used in ICC pin lock
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param oldPassword is the old password
+ * @param newPassword is the new password
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void changeIccLockPassword(String oldPassword, String newPassword,
+ Message onComplete) {
+ mPhone.mCM.changeIccPin(oldPassword, newPassword,
+ mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
+
+ }
+
+ /**
+ * Change the ICC password used in ICC fdn enable
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param oldPassword is the old password
+ * @param newPassword is the new password
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void changeIccFdnPassword(String oldPassword, String newPassword,
+ Message onComplete) {
+ mPhone.mCM.changeIccPin2(oldPassword, newPassword,
+ mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
+
+ }
+
+
+ /**
+ * Returns service provider name stored in ICC card.
+ * If there is no service provider name associated or the record is not
+ * yet available, null will be returned
+ *
+ * Please use this value when display Service Provider Name in idle mode
+ *
+ * Usage of this provider name in the UI is a common carrier requirement.
+ *
+ * Also available via Android property "gsm.sim.operator.alpha"
+ *
+ * @return Service Provider Name stored in ICC card
+ * null if no service provider name associated or the record is not
+ * yet available
+ *
+ */
+ public String getServiceProviderName () {
+ return mPhone.mIccRecords.getServiceProviderName();
+ }
+
+ protected void updateStateProperty() {
+ mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
+ }
+
+ private void getIccCardStatusDone(AsyncResult ar) {
+ if (ar.exception != null) {
+ Log.e(mLogTag,"Error getting ICC status. "
+ + "RIL_REQUEST_GET_ICC_STATUS should "
+ + "never return an error", ar.exception);
+ return;
+ }
+ handleIccCardStatus((IccCardStatus) ar.result);
+ }
+
+ private void handleIccCardStatus(IccCardStatus newCardStatus) {
+ boolean transitionedIntoPinLocked;
+ boolean transitionedIntoAbsent;
+ boolean transitionedIntoNetworkLocked;
+ boolean transitionedIntoPermBlocked;
+ boolean isIccCardRemoved;
+ boolean isIccCardAdded;
+
+ IccCardConstants.State oldState, newState;
+ IccCardConstants.State oldRuimState = getRuimState();
+
+ oldState = mState;
+ mIccCardStatus = newCardStatus;
+ newState = getIccCardState();
+
+ synchronized (mStateMonitor) {
+ mState = newState;
+ updateStateProperty();
+ if (oldState != IccCardConstants.State.READY &&
+ newState == IccCardConstants.State.READY) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_ICC_READY));
+ mReadyRegistrants.notifyRegistrants();
+ } else if (newState.isPinLocked()) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_ICC_LOCKED));
+ }
+ if (oldRuimState != IccCardConstants.State.READY &&
+ getRuimState() == IccCardConstants.State.READY) {
+ mRuimReadyRegistrants.notifyRegistrants();
+ }
+ }
+
+ transitionedIntoPinLocked = (
+ (oldState != IccCardConstants.State.PIN_REQUIRED &&
+ newState == IccCardConstants.State.PIN_REQUIRED)
+ || (oldState != IccCardConstants.State.PUK_REQUIRED &&
+ newState == IccCardConstants.State.PUK_REQUIRED));
+ transitionedIntoAbsent = (oldState != IccCardConstants.State.ABSENT &&
+ newState == IccCardConstants.State.ABSENT);
+ transitionedIntoNetworkLocked = (oldState != IccCardConstants.State.NETWORK_LOCKED
+ && newState == IccCardConstants.State.NETWORK_LOCKED);
+ transitionedIntoPermBlocked = (oldState != IccCardConstants.State.PERM_DISABLED
+ && newState == IccCardConstants.State.PERM_DISABLED);
+ isIccCardRemoved = (oldState != null && oldState.iccCardExist() &&
+ newState == IccCardConstants.State.ABSENT);
+ isIccCardAdded = (oldState == IccCardConstants.State.ABSENT &&
+ newState != null && newState.iccCardExist());
+
+ if (transitionedIntoPinLocked) {
+ if (mDbg) log("Notify SIM pin or puk locked.");
+ mPinLockedRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOCKED,
+ (newState == IccCardConstants.State.PIN_REQUIRED) ?
+ IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN :
+ IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK);
+ } else if (transitionedIntoAbsent) {
+ if (mDbg) log("Notify SIM missing.");
+ mAbsentRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
+ } else if (transitionedIntoNetworkLocked) {
+ if (mDbg) log("Notify SIM network locked.");
+ mNetworkLockedRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_LOCKED,
+ IccCardConstants.INTENT_VALUE_LOCKED_NETWORK);
+ } else if (transitionedIntoPermBlocked) {
+ if (mDbg) log("Notify SIM permanently disabled.");
+ broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_ABSENT,
+ IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED);
+ }
+
+ if (isIccCardRemoved) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
+ } else if (isIccCardAdded) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
+ }
+
+ // Call onReady Record(s) on the IccCard becomes ready (not NV)
+ if (oldState != IccCardConstants.State.READY && newState == IccCardConstants.State.READY &&
+ (is3gpp || isSubscriptionFromIccCard)) {
+ if (!(mIccFileHandler instanceof CdmaLteUiccFileHandler)) {
+ // CdmaLteUicc File Handler deals with both USIM and CSIM.
+ // Do not lock onto one AID for now.
+ mIccFileHandler.setAid(getAid());
+ }
+ mIccRecords.onReady();
+ }
+ }
+
+ private void onIccSwap(boolean isAdded) {
+ // TODO: Here we assume the device can't handle SIM hot-swap
+ // and has to reboot. We may want to add a property,
+ // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
+ // hot-swap.
+ DialogInterface.OnClickListener listener = null;
+
+
+ // TODO: SimRecords is not reset while SIM ABSENT (only reset while
+ // Radio_off_or_not_available). Have to reset in both both
+ // added or removed situation.
+ listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mDbg) log("Reboot due to SIM swap");
+ PowerManager pm = (PowerManager) mPhone.getContext()
+ .getSystemService(Context.POWER_SERVICE);
+ pm.reboot("SIM is added.");
+ }
+ }
+
+ };
+
+ Resources r = Resources.getSystem();
+
+ String title = (isAdded) ? r.getString(R.string.sim_added_title) :
+ r.getString(R.string.sim_removed_title);
+ String message = (isAdded) ? r.getString(R.string.sim_added_message) :
+ r.getString(R.string.sim_removed_message);
+ String buttonTxt = r.getString(R.string.sim_restart_button);
+
+ AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext())
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(buttonTxt, listener)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ /**
+ * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
+ * @param ar is asyncResult of Query_Facility_Locked
+ */
+ private void onQueryFdnEnabled(AsyncResult ar) {
+ if(ar.exception != null) {
+ if(mDbg) log("Error in querying facility lock:" + ar.exception);
+ return;
+ }
+
+ int[] ints = (int[])ar.result;
+ if(ints.length != 0) {
+ mIccFdnEnabled = (0!=ints[0]);
+ if(mDbg) log("Query facility lock : " + mIccFdnEnabled);
+ } else {
+ Log.e(mLogTag, "[IccCard] Bogus facility lock response");
+ }
+ }
+
+ /**
+ * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
+ * @param ar is asyncResult of Query_Facility_Locked
+ */
+ private void onQueryFacilityLock(AsyncResult ar) {
+ if(ar.exception != null) {
+ if (mDbg) log("Error in querying facility lock:" + ar.exception);
+ return;
+ }
+
+ int[] ints = (int[])ar.result;
+ if(ints.length != 0) {
+ mIccPinLocked = (0!=ints[0]);
+ if(mDbg) log("Query facility lock : " + mIccPinLocked);
+ } else {
+ Log.e(mLogTag, "[IccCard] Bogus facility lock response");
+ }
+ }
+
+ public void broadcastIccStateChangedIntent(String value, String reason) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(PhoneConstants.PHONE_NAME_KEY, mPhone.getPhoneName());
+ intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
+ intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
+ if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
+ + " reason " + reason);
+ ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
+ }
+
+ protected Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg){
+ AsyncResult ar;
+ int serviceClassX;
+
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX;
+
+ if (!mPhone.mIsTheCurrentActivePhone) {
+ Log.e(mLogTag, "Received message " + msg + "[" + msg.what
+ + "] while being destroyed. Ignoring.");
+ return;
+ }
+
+ switch (msg.what) {
+ case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
+ mState = null;
+ updateStateProperty();
+ broadcastIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
+ null);
+ break;
+ case EVENT_RADIO_ON:
+ if (!is3gpp) {
+ handleCdmaSubscriptionSource();
+ }
+ mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ break;
+ case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
+ handleCdmaSubscriptionSource();
+ break;
+ case EVENT_ICC_READY:
+ if(isSubscriptionFromIccCard) {
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
+ }
+ break;
+ case EVENT_ICC_LOCKED:
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
+ break;
+ case EVENT_GET_ICC_STATUS_DONE:
+ ar = (AsyncResult)msg.obj;
+
+ getIccCardStatusDone(ar);
+ break;
+ case EVENT_PINPUK_DONE:
+ // a PIN/PUK/PIN2/PUK2/Network Personalization
+ // request has completed. ar.userObj is the response Message
+ // Repoll before returning
+ ar = (AsyncResult)msg.obj;
+ // TODO should abstract these exceptions
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ mPhone.mCM.getIccCardStatus(
+ obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
+ break;
+ case EVENT_REPOLL_STATUS_DONE:
+ // Finished repolling status after PIN operation
+ // ar.userObj is the response messaeg
+ // ar.userObj.obj is already an AsyncResult with an
+ // appropriate exception filled in if applicable
+
+ ar = (AsyncResult)msg.obj;
+ getIccCardStatusDone(ar);
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_QUERY_FACILITY_LOCK_DONE:
+ ar = (AsyncResult)msg.obj;
+ onQueryFacilityLock(ar);
+ break;
+ case EVENT_QUERY_FACILITY_FDN_DONE:
+ ar = (AsyncResult)msg.obj;
+ onQueryFdnEnabled(ar);
+ break;
+ case EVENT_CHANGE_FACILITY_LOCK_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ mIccPinLocked = mDesiredPinLocked;
+ if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
+ "mIccPinLocked= " + mIccPinLocked);
+ } else {
+ Log.e(mLogTag, "Error change facility lock with exception "
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_CHANGE_FACILITY_FDN_DONE:
+ ar = (AsyncResult)msg.obj;
+
+ if (ar.exception == null) {
+ mIccFdnEnabled = mDesiredFdnEnabled;
+ if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
+ "mIccFdnEnabled=" + mIccFdnEnabled);
+ } else {
+ Log.e(mLogTag, "Error change facility fdn with exception "
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_CHANGE_ICC_PASSWORD_DONE:
+ ar = (AsyncResult)msg.obj;
+ if(ar.exception != null) {
+ Log.e(mLogTag, "Error in change sim password with exception"
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_ICC_STATUS_CHANGED:
+ Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED");
+ mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ break;
+ case EVENT_CARD_REMOVED:
+ onIccSwap(false);
+ break;
+ case EVENT_CARD_ADDED:
+ onIccSwap(true);
+ break;
+ default:
+ Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
+ }
+ }
+ };
+
+ private void handleCdmaSubscriptionSource() {
+ if(mCdmaSSM != null) {
+ int newSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
+
+ Log.d(mLogTag, "Received Cdma subscription source: " + newSubscriptionSource);
+
+ boolean isNewSubFromRuim =
+ (newSubscriptionSource == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
+
+ if (isNewSubFromRuim != isSubscriptionFromIccCard) {
+ isSubscriptionFromIccCard = isNewSubFromRuim;
+ // Parse the Stored IccCardStatus Message to set mState correctly.
+ handleIccCardStatus(mIccCardStatus);
+ }
+ }
+ }
+
+ public IccCardConstants.State getIccCardState() {
+ if(!is3gpp && !isSubscriptionFromIccCard) {
+ // CDMA can get subscription from NV. In that case,
+ // subscription is ready as soon as Radio is ON.
+ return IccCardConstants.State.READY;
+ }
+
+ if (mIccCardStatus == null) {
+ Log.e(mLogTag, "[IccCard] IccCardStatus is null");
+ return IccCardConstants.State.ABSENT;
+ }
+
+ // this is common for all radio technologies
+ if (!mIccCardStatus.getCardState().isCardPresent()) {
+ return IccCardConstants.State.ABSENT;
+ }
+
+ RadioState currentRadioState = mPhone.mCM.getRadioState();
+ // check radio technology
+ if( currentRadioState == RadioState.RADIO_OFF ||
+ currentRadioState == RadioState.RADIO_UNAVAILABLE) {
+ return IccCardConstants.State.NOT_READY;
+ }
+
+ if( currentRadioState == RadioState.RADIO_ON ) {
+ IccCardConstants.State csimState =
+ getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
+ IccCardConstants.State usimState =
+ getAppState(mIccCardStatus.getGsmUmtsSubscriptionAppIndex());
+
+ if(mDbg) log("USIM=" + usimState + " CSIM=" + csimState);
+
+ if (mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ // UICC card contains both USIM and CSIM
+ // Return consolidated status
+ return getConsolidatedState(csimState, usimState, csimState);
+ }
+
+ // check for CDMA radio technology
+ if (!is3gpp) {
+ return csimState;
+ }
+ return usimState;
+ }
+
+ return IccCardConstants.State.ABSENT;
+ }
+
+ private IccCardConstants.State getAppState(int appIndex) {
+ IccCardApplication app;
+ if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
+ app = mIccCardStatus.getApplication(appIndex);
+ } else {
+ Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + appIndex);
+ return IccCardConstants.State.ABSENT;
+ }
+
+ if (app == null) {
+ Log.e(mLogTag, "[IccCard] Subscription Application in not present");
+ return IccCardConstants.State.ABSENT;
+ }
+
+ // check if PIN required
+ if (app.pin1.isPermBlocked()) {
+ return IccCardConstants.State.PERM_DISABLED;
+ }
+ if (app.app_state.isPinRequired()) {
+ return IccCardConstants.State.PIN_REQUIRED;
+ }
+ if (app.app_state.isPukRequired()) {
+ return IccCardConstants.State.PUK_REQUIRED;
+ }
+ if (app.app_state.isSubscriptionPersoEnabled()) {
+ return IccCardConstants.State.NETWORK_LOCKED;
+ }
+ if (app.app_state.isAppReady()) {
+ return IccCardConstants.State.READY;
+ }
+ if (app.app_state.isAppNotReady()) {
+ return IccCardConstants.State.NOT_READY;
+ }
+ return IccCardConstants.State.NOT_READY;
+ }
+
+ private IccCardConstants.State getConsolidatedState(IccCardConstants.State left,
+ IccCardConstants.State right, IccCardConstants.State preferredState) {
+ // Check if either is absent.
+ if (right == IccCardConstants.State.ABSENT) return left;
+ if (left == IccCardConstants.State.ABSENT) return right;
+
+ // Only if both are ready, return ready
+ if ((left == IccCardConstants.State.READY) && (right == IccCardConstants.State.READY)) {
+ return IccCardConstants.State.READY;
+ }
+
+ // Case one is ready, but the other is not.
+ if (((right == IccCardConstants.State.NOT_READY) &&
+ (left == IccCardConstants.State.READY)) ||
+ ((left == IccCardConstants.State.NOT_READY) &&
+ (right == IccCardConstants.State.READY))) {
+ return IccCardConstants.State.NOT_READY;
+ }
+
+ // At this point, the other state is assumed to be one of locked state
+ if (right == IccCardConstants.State.NOT_READY) return left;
+ if (left == IccCardConstants.State.NOT_READY) return right;
+
+ // At this point, FW currently just assumes the status will be
+ // consistent across the applications...
+ return preferredState;
+ }
+
+ public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
+ if (mIccCardStatus == null) return false;
+
+ for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
+ IccCardApplication app = mIccCardStatus.getApplication(i);
+ if (app != null && app.app_type == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true if a ICC card is present
+ */
+ public boolean hasIccCard() {
+ if (mIccCardStatus == null) {
+ return false;
+ } else {
+ // Returns ICC card status for both GSM and CDMA mode
+ return mIccCardStatus.getCardState().isCardPresent();
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(mLogTag, "[IccCard] " + msg);
+ }
+
+ protected int getCurrentApplicationIndex() {
+ if (is3gpp) {
+ return mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
+ } else {
+ return mIccCardStatus.getCdmaSubscriptionAppIndex();
+ }
+ }
+
+ public String getAid() {
+ String aid = "";
+ if (mIccCardStatus == null) {
+ return aid;
+ }
+
+ int appIndex = getCurrentApplicationIndex();
+
+ if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
+ IccCardApplication app = mIccCardStatus.getApplication(appIndex);
+ if (app != null) {
+ aid = app.aid;
+ } else {
+ Log.e(mLogTag, "[IccCard] getAid: no current application index=" + appIndex);
+ }
+ } else {
+ Log.e(mLogTag, "[IccCard] getAid: Invalid Subscription Application index=" + appIndex);
+ }
+
+ return aid;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccCardApplication.java b/src/java/com/android/internal/telephony/IccCardApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..abb740ef0c32af9b9cff10ae407fc43d86bf80ee
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccCardApplication.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.IccCardStatus.PinState;
+
+
+/**
+ * See also RIL_AppStatus in include/telephony/ril.h
+ *
+ * {@hide}
+ */
+public class IccCardApplication {
+ public enum AppType{
+ APPTYPE_UNKNOWN,
+ APPTYPE_SIM,
+ APPTYPE_USIM,
+ APPTYPE_RUIM,
+ APPTYPE_CSIM,
+ APPTYPE_ISIM
+ };
+
+ public enum AppState{
+ APPSTATE_UNKNOWN,
+ APPSTATE_DETECTED,
+ APPSTATE_PIN,
+ APPSTATE_PUK,
+ APPSTATE_SUBSCRIPTION_PERSO,
+ APPSTATE_READY;
+
+ boolean isPinRequired() {
+ return this == APPSTATE_PIN;
+ }
+
+ boolean isPukRequired() {
+ return this == APPSTATE_PUK;
+ }
+
+ boolean isSubscriptionPersoEnabled() {
+ return this == APPSTATE_SUBSCRIPTION_PERSO;
+ }
+
+ boolean isAppReady() {
+ return this == APPSTATE_READY;
+ }
+
+ boolean isAppNotReady() {
+ return this == APPSTATE_UNKNOWN ||
+ this == APPSTATE_DETECTED;
+ }
+ };
+
+ public enum PersoSubState{
+ PERSOSUBSTATE_UNKNOWN,
+ PERSOSUBSTATE_IN_PROGRESS,
+ PERSOSUBSTATE_READY,
+ PERSOSUBSTATE_SIM_NETWORK,
+ PERSOSUBSTATE_SIM_NETWORK_SUBSET,
+ PERSOSUBSTATE_SIM_CORPORATE,
+ PERSOSUBSTATE_SIM_SERVICE_PROVIDER,
+ PERSOSUBSTATE_SIM_SIM,
+ PERSOSUBSTATE_SIM_NETWORK_PUK,
+ PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK,
+ PERSOSUBSTATE_SIM_CORPORATE_PUK,
+ PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK,
+ PERSOSUBSTATE_SIM_SIM_PUK,
+ PERSOSUBSTATE_RUIM_NETWORK1,
+ PERSOSUBSTATE_RUIM_NETWORK2,
+ PERSOSUBSTATE_RUIM_HRPD,
+ PERSOSUBSTATE_RUIM_CORPORATE,
+ PERSOSUBSTATE_RUIM_SERVICE_PROVIDER,
+ PERSOSUBSTATE_RUIM_RUIM,
+ PERSOSUBSTATE_RUIM_NETWORK1_PUK,
+ PERSOSUBSTATE_RUIM_NETWORK2_PUK,
+ PERSOSUBSTATE_RUIM_HRPD_PUK,
+ PERSOSUBSTATE_RUIM_CORPORATE_PUK,
+ PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK,
+ PERSOSUBSTATE_RUIM_RUIM_PUK;
+
+ boolean isPersoSubStateUnknown() {
+ return this == PERSOSUBSTATE_UNKNOWN;
+ }
+ };
+
+ public AppType app_type;
+ public AppState app_state;
+ // applicable only if app_state == RIL_APPSTATE_SUBSCRIPTION_PERSO
+ public PersoSubState perso_substate;
+ // null terminated string, e.g., from 0xA0, 0x00 -> 0x41, 0x30, 0x30, 0x30 */
+ public String aid;
+ // null terminated string
+ public String app_label;
+ // applicable to USIM and CSIM
+ public int pin1_replaced;
+ public PinState pin1;
+ public PinState pin2;
+
+ AppType AppTypeFromRILInt(int type) {
+ AppType newType;
+ /* RIL_AppType ril.h */
+ switch(type) {
+ case 0: newType = AppType.APPTYPE_UNKNOWN; break;
+ case 1: newType = AppType.APPTYPE_SIM; break;
+ case 2: newType = AppType.APPTYPE_USIM; break;
+ case 3: newType = AppType.APPTYPE_RUIM; break;
+ case 4: newType = AppType.APPTYPE_CSIM; break;
+ case 5: newType = AppType.APPTYPE_ISIM; break;
+ default:
+ throw new RuntimeException(
+ "Unrecognized RIL_AppType: " +type);
+ }
+ return newType;
+ }
+
+ AppState AppStateFromRILInt(int state) {
+ AppState newState;
+ /* RIL_AppState ril.h */
+ switch(state) {
+ case 0: newState = AppState.APPSTATE_UNKNOWN; break;
+ case 1: newState = AppState.APPSTATE_DETECTED; break;
+ case 2: newState = AppState.APPSTATE_PIN; break;
+ case 3: newState = AppState.APPSTATE_PUK; break;
+ case 4: newState = AppState.APPSTATE_SUBSCRIPTION_PERSO; break;
+ case 5: newState = AppState.APPSTATE_READY; break;
+ default:
+ throw new RuntimeException(
+ "Unrecognized RIL_AppState: " +state);
+ }
+ return newState;
+ }
+
+ PersoSubState PersoSubstateFromRILInt(int substate) {
+ PersoSubState newSubState;
+ /* RIL_PeroSubstate ril.h */
+ switch(substate) {
+ case 0: newSubState = PersoSubState.PERSOSUBSTATE_UNKNOWN; break;
+ case 1: newSubState = PersoSubState.PERSOSUBSTATE_IN_PROGRESS; break;
+ case 2: newSubState = PersoSubState.PERSOSUBSTATE_READY; break;
+ case 3: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK; break;
+ case 4: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_SUBSET; break;
+ case 5: newSubState = PersoSubState.PERSOSUBSTATE_SIM_CORPORATE; break;
+ case 6: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SERVICE_PROVIDER; break;
+ case 7: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SIM; break;
+ case 8: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_PUK; break;
+ case 9: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK; break;
+ case 10: newSubState = PersoSubState.PERSOSUBSTATE_SIM_CORPORATE_PUK; break;
+ case 11: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK; break;
+ case 12: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SIM_PUK; break;
+ case 13: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK1; break;
+ case 14: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK2; break;
+ case 15: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_HRPD; break;
+ case 16: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_CORPORATE; break;
+ case 17: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_SERVICE_PROVIDER; break;
+ case 18: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_RUIM; break;
+ case 19: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK1_PUK; break;
+ case 20: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK2_PUK; break;
+ case 21: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_HRPD_PUK ; break;
+ case 22: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_CORPORATE_PUK; break;
+ case 23: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK; break;
+ case 24: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_RUIM_PUK; break;
+ default:
+ throw new RuntimeException(
+ "Unrecognized RIL_PersoSubstate: " +substate);
+ }
+ return newSubState;
+ }
+
+ PinState PinStateFromRILInt(int state) {
+ PinState newPinState;
+ switch(state) {
+ case 0:
+ newPinState = PinState.PINSTATE_UNKNOWN;
+ break;
+ case 1:
+ newPinState = PinState.PINSTATE_ENABLED_NOT_VERIFIED;
+ break;
+ case 2:
+ newPinState = PinState.PINSTATE_ENABLED_VERIFIED;
+ break;
+ case 3:
+ newPinState = PinState.PINSTATE_DISABLED;
+ break;
+ case 4:
+ newPinState = PinState.PINSTATE_ENABLED_BLOCKED;
+ break;
+ case 5:
+ newPinState = PinState.PINSTATE_ENABLED_PERM_BLOCKED;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_PinState: " + state);
+ }
+ return newPinState;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("{").append(app_type).append(",").append(app_state);
+ if (app_state == AppState.APPSTATE_SUBSCRIPTION_PERSO) {
+ sb.append(",").append(perso_substate);
+ }
+ if (app_type == AppType.APPTYPE_CSIM ||
+ app_type == AppType.APPTYPE_USIM ||
+ app_type == AppType.APPTYPE_ISIM) {
+ sb.append(",pin1=").append(pin1);
+ sb.append(",pin2=").append(pin2);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccCardStatus.java b/src/java/com/android/internal/telephony/IccCardStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3bdd76b6e00ff1c3c1923f77bacad9aa15af62f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccCardStatus.java
@@ -0,0 +1,188 @@
+/*
+ * 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 java.util.ArrayList;
+
+/**
+ * See also RIL_CardStatus in include/telephony/ril.h
+ *
+ * {@hide}
+ */
+public class IccCardStatus {
+ public static final int CARD_MAX_APPS = 8;
+
+ public enum CardState {
+ CARDSTATE_ABSENT,
+ CARDSTATE_PRESENT,
+ CARDSTATE_ERROR;
+
+ boolean isCardPresent() {
+ return this == CARDSTATE_PRESENT;
+ }
+ }
+
+ public enum PinState {
+ PINSTATE_UNKNOWN,
+ PINSTATE_ENABLED_NOT_VERIFIED,
+ PINSTATE_ENABLED_VERIFIED,
+ PINSTATE_DISABLED,
+ PINSTATE_ENABLED_BLOCKED,
+ PINSTATE_ENABLED_PERM_BLOCKED;
+
+ boolean isPermBlocked() {
+ return this == PINSTATE_ENABLED_PERM_BLOCKED;
+ }
+
+ boolean isPinRequired() {
+ return this == PINSTATE_ENABLED_NOT_VERIFIED;
+ }
+
+ boolean isPukRequired() {
+ return this == PINSTATE_ENABLED_BLOCKED;
+ }
+ }
+
+ private CardState mCardState;
+ private PinState mUniversalPinState;
+ private int mGsmUmtsSubscriptionAppIndex;
+ private int mCdmaSubscriptionAppIndex;
+ private int mImsSubscriptionAppIndex;
+ private int mNumApplications;
+
+ private ArrayList mApplications =
+ new ArrayList(CARD_MAX_APPS);
+
+ public CardState getCardState() {
+ return mCardState;
+ }
+
+ public void setCardState(int state) {
+ switch(state) {
+ case 0:
+ mCardState = CardState.CARDSTATE_ABSENT;
+ break;
+ case 1:
+ mCardState = CardState.CARDSTATE_PRESENT;
+ break;
+ case 2:
+ mCardState = CardState.CARDSTATE_ERROR;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_CardState: " + state);
+ }
+ }
+
+ public PinState getUniversalPinState() {
+ return mUniversalPinState;
+ }
+
+ public void setUniversalPinState(int state) {
+ switch(state) {
+ case 0:
+ mUniversalPinState = PinState.PINSTATE_UNKNOWN;
+ break;
+ case 1:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_NOT_VERIFIED;
+ break;
+ case 2:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_VERIFIED;
+ break;
+ case 3:
+ mUniversalPinState = PinState.PINSTATE_DISABLED;
+ break;
+ case 4:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_BLOCKED;
+ break;
+ case 5:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_PERM_BLOCKED;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_PinState: " + state);
+ }
+ }
+
+ public int getGsmUmtsSubscriptionAppIndex() {
+ return mGsmUmtsSubscriptionAppIndex;
+ }
+
+ public void setGsmUmtsSubscriptionAppIndex(int gsmUmtsSubscriptionAppIndex) {
+ mGsmUmtsSubscriptionAppIndex = gsmUmtsSubscriptionAppIndex;
+ }
+
+ public int getCdmaSubscriptionAppIndex() {
+ return mCdmaSubscriptionAppIndex;
+ }
+
+ public void setCdmaSubscriptionAppIndex(int cdmaSubscriptionAppIndex) {
+ mCdmaSubscriptionAppIndex = cdmaSubscriptionAppIndex;
+ }
+
+ public int getImsSubscriptionAppIndex() {
+ return mImsSubscriptionAppIndex;
+ }
+
+ public void setImsSubscriptionAppIndex(int imsSubscriptionAppIndex) {
+ mImsSubscriptionAppIndex = imsSubscriptionAppIndex;
+ }
+
+ public int getNumApplications() {
+ return mNumApplications;
+ }
+
+ public void setNumApplications(int numApplications) {
+ mNumApplications = numApplications;
+ }
+
+ public void addApplication(IccCardApplication application) {
+ mApplications.add(application);
+ }
+
+ public IccCardApplication getApplication(int index) {
+ return mApplications.get(index);
+ }
+
+ @Override
+ public String toString() {
+ IccCardApplication app;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("IccCardState {").append(mCardState).append(",")
+ .append(mUniversalPinState)
+ .append(",num_apps=").append(mNumApplications)
+ .append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
+ if (mGsmUmtsSubscriptionAppIndex >=0
+ && mGsmUmtsSubscriptionAppIndex =0
+ && mCdmaSubscriptionAppIndex results;
+
+ LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) {
+ this.efid = efid;
+ this.recordNum = recordNum;
+ this.onLoaded = onLoaded;
+ this.loadAll = false;
+ }
+
+ LoadLinearFixedContext(int efid, Message onLoaded) {
+ this.efid = efid;
+ this.recordNum = 1;
+ this.loadAll = true;
+ this.onLoaded = onLoaded;
+ }
+ }
+
+ /**
+ * Default constructor
+ */
+ protected IccFileHandler(IccCard card, String aid, CommandsInterface ci) {
+ mParentCard = card;
+ mAid = aid;
+ mCi = ci;
+ }
+
+ public void dispose() {
+ }
+
+ //***** Public Methods
+
+ /**
+ * Load a record from a SIM Linear Fixed EF
+ *
+ * @param fileid EF id
+ * @param recordNum 1-based (not 0-based) record number
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+ *
+ */
+ 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);
+ }
+
+ /**
+ * Load a image instance record from a SIM Linear Fixed EF-IMG
+ *
+ * @param recordNum 1-based (not 0-based) record number
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+ *
+ */
+ public void loadEFImgLinearFixed(int recordNum, Message onLoaded) {
+ Message response = obtainMessage(EVENT_READ_IMG_DONE,
+ new LoadLinearFixedContext(IccConstants.EF_IMG, recordNum,
+ onLoaded));
+
+ // TODO(): Verify when path changes are done.
+ mCi.iccIOForApp(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
+ recordNum, READ_RECORD_MODE_ABSOLUTE,
+ GET_RESPONSE_EF_IMG_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);
+ }
+
+ /**
+ * Load all records from a SIM Linear Fixed EF
+ *
+ * @param fileid EF id
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is an ArrayList
+ *
+ */
+ 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);
+ }
+
+ /**
+ * Load a SIM Transparent EF
+ *
+ * @param fileid EF id
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+ *
+ */
+
+ public void loadEFTransparent(int fileid, Message onLoaded) {
+ Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE,
+ fileid, 0, onLoaded);
+
+ mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+ }
+
+ /**
+ * Load a SIM Transparent EF-IMG. Used right after loadEFImgLinearFixed to
+ * retrive STK's icon data.
+ *
+ * @param fileid EF id
+ * @param onLoaded
+ *
+ * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+ *
+ */
+ public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset,
+ int length, Message onLoaded) {
+ Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
+ onLoaded);
+
+ mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
+ length, null, null, mAid, response);
+ }
+
+ /**
+ * 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
+ * @param onComplete onComplete.obj will be an AsyncResult
+ * onComplete.obj.userObj will be a IccIoResult on success
+ */
+ public void updateEFLinearFixed(int fileid, int recordNum, byte[] data,
+ String pin2, Message onComplete) {
+ mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
+ recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
+ IccUtils.bytesToHexString(data), pin2, mAid, onComplete);
+ }
+
+ /**
+ * Update a transparent EF
+ * @param fileid EF id
+ * @param data must be exactly as long as the EF
+ */
+ public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {
+ mCi.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
+ 0, 0, data.length,
+ IccUtils.bytesToHexString(data), null, mAid, onComplete);
+ }
+
+
+ //***** Abstract Methods
+
+
+ //***** Private Methods
+
+ private void sendResult(Message response, Object result, Throwable ex) {
+ if (response == null) {
+ return;
+ }
+
+ AsyncResult.forMessage(response, result, ex);
+
+ response.sendToTarget();
+ }
+
+ //***** Overridden from Handler
+
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ IccIoResult result;
+ Message response = null;
+ String str;
+ LoadLinearFixedContext lc;
+
+ IccException iccException;
+ byte data[];
+ int size;
+ int fileid;
+ int recordNum;
+ int recordSize[];
+
+ try {
+ switch (msg.what) {
+ case EVENT_READ_IMG_DONE:
+ ar = (AsyncResult) msg.obj;
+ lc = (LoadLinearFixedContext) ar.userObj;
+ result = (IccIoResult) ar.result;
+ response = lc.onLoaded;
+
+ iccException = result.getException();
+ if (iccException != null) {
+ sendResult(response, result.payload, ar.exception);
+ }
+ break;
+ case EVENT_READ_ICON_DONE:
+ ar = (AsyncResult) msg.obj;
+ response = (Message) ar.userObj;
+ result = (IccIoResult) ar.result;
+
+ iccException = result.getException();
+ if (iccException != null) {
+ sendResult(response, result.payload, ar.exception);
+ }
+ break;
+ case EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE:
+ ar = (AsyncResult)msg.obj;
+ lc = (LoadLinearFixedContext) ar.userObj;
+ result = (IccIoResult) ar.result;
+ response = lc.onLoaded;
+
+ if (ar.exception != null) {
+ sendResult(response, null, ar.exception);
+ break;
+ }
+
+ iccException = result.getException();
+ if (iccException != null) {
+ sendResult(response, null, iccException);
+ break;
+ }
+
+ data = result.payload;
+
+ if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE] ||
+ EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) {
+ throw new IccFileTypeMismatch();
+ }
+
+ recordSize = new int[3];
+ recordSize[0] = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;
+ recordSize[1] = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
+ + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
+ recordSize[2] = recordSize[1] / recordSize[0];
+
+ sendResult(response, recordSize, null);
+ break;
+ case EVENT_GET_RECORD_SIZE_DONE:
+ ar = (AsyncResult)msg.obj;
+ lc = (LoadLinearFixedContext) ar.userObj;
+ result = (IccIoResult) ar.result;
+ response = lc.onLoaded;
+
+ if (ar.exception != null) {
+ sendResult(response, null, ar.exception);
+ break;
+ }
+
+ iccException = result.getException();
+
+ if (iccException != null) {
+ sendResult(response, null, iccException);
+ break;
+ }
+
+ data = result.payload;
+ fileid = lc.efid;
+ recordNum = lc.recordNum;
+
+ if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
+ throw new IccFileTypeMismatch();
+ }
+
+ if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) {
+ throw new IccFileTypeMismatch();
+ }
+
+ lc.recordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF;
+
+ size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
+ + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
+
+ lc.countRecords = size / lc.recordSize;
+
+ if (lc.loadAll) {
+ lc.results = new ArrayList(lc.countRecords);
+ }
+
+ mCi.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+ lc.recordNum,
+ READ_RECORD_MODE_ABSOLUTE,
+ lc.recordSize, null, null, mAid,
+ obtainMessage(EVENT_READ_RECORD_DONE, lc));
+ break;
+ case EVENT_GET_BINARY_SIZE_DONE:
+ ar = (AsyncResult)msg.obj;
+ response = (Message) ar.userObj;
+ result = (IccIoResult) ar.result;
+
+ if (ar.exception != null) {
+ sendResult(response, null, ar.exception);
+ break;
+ }
+
+ iccException = result.getException();
+
+ if (iccException != null) {
+ sendResult(response, null, iccException);
+ break;
+ }
+
+ data = result.payload;
+
+ fileid = msg.arg1;
+
+ if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
+ throw new IccFileTypeMismatch();
+ }
+
+ if (EF_TYPE_TRANSPARENT != data[RESPONSE_DATA_STRUCTURE]) {
+ throw new IccFileTypeMismatch();
+ }
+
+ size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
+ + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
+
+ mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+ 0, 0, size, null, null, mAid,
+ obtainMessage(EVENT_READ_BINARY_DONE,
+ fileid, 0, response));
+ break;
+
+ case EVENT_READ_RECORD_DONE:
+
+ ar = (AsyncResult)msg.obj;
+ lc = (LoadLinearFixedContext) ar.userObj;
+ result = (IccIoResult) ar.result;
+ response = lc.onLoaded;
+
+ if (ar.exception != null) {
+ sendResult(response, null, ar.exception);
+ break;
+ }
+
+ iccException = result.getException();
+
+ if (iccException != null) {
+ sendResult(response, null, iccException);
+ break;
+ }
+
+ if (!lc.loadAll) {
+ sendResult(response, result.payload, null);
+ } else {
+ lc.results.add(result.payload);
+
+ lc.recordNum++;
+
+ if (lc.recordNum > lc.countRecords) {
+ sendResult(response, lc.results, null);
+ } else {
+ mCi.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+ lc.recordNum,
+ READ_RECORD_MODE_ABSOLUTE,
+ lc.recordSize, null, null, mAid,
+ obtainMessage(EVENT_READ_RECORD_DONE, lc));
+ }
+ }
+
+ break;
+
+ case EVENT_READ_BINARY_DONE:
+ ar = (AsyncResult)msg.obj;
+ response = (Message) ar.userObj;
+ result = (IccIoResult) ar.result;
+
+ if (ar.exception != null) {
+ sendResult(response, null, ar.exception);
+ break;
+ }
+
+ iccException = result.getException();
+
+ if (iccException != null) {
+ sendResult(response, null, iccException);
+ break;
+ }
+
+ sendResult(response, result.payload, null);
+ break;
+
+ }} catch (Exception exc) {
+ if (response != null) {
+ sendResult(response, null, exc);
+ } else {
+ loge("uncaught exception" + exc);
+ }
+ }
+ }
+
+ /**
+ * Returns the root path of the EF file.
+ * i.e returns MasterFile + DFfile as a string.
+ * Ex: For EF_ADN on a SIM, it will return "3F007F10"
+ * This function handles only EFids that are common to
+ * RUIM, SIM, USIM and other types of Icc cards.
+ *
+ * @param efId
+ * @return root path of the file.
+ */
+ protected String getCommonIccEFPath(int efid) {
+ switch(efid) {
+ case EF_ADN:
+ case EF_FDN:
+ case EF_MSISDN:
+ case EF_SDN:
+ case EF_EXT1:
+ case EF_EXT2:
+ case EF_EXT3:
+ return MF_SIM + DF_TELECOM;
+
+ case EF_ICCID:
+ case EF_PL:
+ return MF_SIM;
+ case EF_IMG:
+ return MF_SIM + DF_TELECOM + DF_GRAPHICS;
+ }
+ return null;
+ }
+
+ protected abstract String getEFPath(int efid);
+ protected abstract void logd(String s);
+
+ protected abstract void loge(String s);
+ protected void setAid(String aid) {
+ mAid = aid;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/IccFileNotFound.java b/src/java/com/android/internal/telephony/IccFileNotFound.java
new file mode 100644
index 0000000000000000000000000000000000000000..915cea688284ed46b903c2ba74a67adaad12d63d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccFileNotFound.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class IccFileNotFound extends IccException {
+ IccFileNotFound() {
+
+ }
+
+ IccFileNotFound(String s) {
+ super(s);
+ }
+
+ IccFileNotFound(int ef) {
+ super("ICC EF Not Found 0x" + Integer.toHexString(ef));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccFileTypeMismatch.java b/src/java/com/android/internal/telephony/IccFileTypeMismatch.java
new file mode 100644
index 0000000000000000000000000000000000000000..66fcfa9d568d0f8896aaf03794051b2b3cb9a91a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccFileTypeMismatch.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class IccFileTypeMismatch extends IccException {
+ public IccFileTypeMismatch() {
+
+ }
+
+ public IccFileTypeMismatch(String s) {
+ super(s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccIoResult.java b/src/java/com/android/internal/telephony/IccIoResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..7043da53f0e0970b3dd25239844fc444b3e067e0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccIoResult.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public class
+IccIoResult {
+ public int sw1;
+ public int sw2;
+
+ public byte[] payload;
+
+ public IccIoResult(int sw1, int sw2, byte[] payload) {
+ this.sw1 = sw1;
+ this.sw2 = sw2;
+ this.payload = payload;
+ }
+
+ public IccIoResult(int sw1, int sw2, String hexString) {
+ this(sw1, sw2, IccUtils.hexStringToBytes(hexString));
+ }
+
+ public String toString() {
+ return "IccIoResponse sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
+ + Integer.toHexString(sw2);
+ }
+
+ /**
+ * true if this operation was successful
+ * See GSM 11.11 Section 9.4
+ * (the fun stuff is absent in 51.011)
+ */
+ public boolean success() {
+ return sw1 == 0x90 || sw1 == 0x91 || sw1 == 0x9e || sw1 == 0x9f;
+ }
+
+ /**
+ * Returns exception on error or null if success
+ */
+ public IccException getException() {
+ if (success()) return null;
+
+ switch (sw1) {
+ case 0x94:
+ if (sw2 == 0x08) {
+ return new IccFileTypeMismatch();
+ } else {
+ return new IccFileNotFound();
+ }
+ default:
+ return new IccException("sw1:" + sw1 + " sw2:" + sw2);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..45562ca96e44bda75bb3a45d213239d542edfffd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -0,0 +1,289 @@
+/*
+ * 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.content.pm.PackageManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ServiceManager;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * SimPhoneBookInterfaceManager to provide an inter-process communication to
+ * access ADN-like SIM records.
+ */
+public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
+ protected static final boolean DBG = true;
+
+ protected PhoneBase phone;
+ protected AdnRecordCache adnCache;
+ protected final Object mLock = new Object();
+ protected int recordSize[];
+ protected boolean success;
+ protected List records;
+
+ protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false;
+
+ protected static final int EVENT_GET_SIZE_DONE = 1;
+ protected static final int EVENT_LOAD_DONE = 2;
+ protected static final int EVENT_UPDATE_DONE = 3;
+
+ protected Handler mBaseHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case EVENT_GET_SIZE_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ if (ar.exception == null) {
+ recordSize = (int[])ar.result;
+ // recordSize[0] is the record length
+ // recordSize[1] is the total length of the EF file
+ // recordSize[2] is the number of records in the EF file
+ logd("GET_RECORD_SIZE Size " + recordSize[0] +
+ " total " + recordSize[1] +
+ " #record " + recordSize[2]);
+ }
+ notifyPending(ar);
+ }
+ break;
+ case EVENT_UPDATE_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ success = (ar.exception == null);
+ notifyPending(ar);
+ }
+ break;
+ case EVENT_LOAD_DONE:
+ ar = (AsyncResult)msg.obj;
+ synchronized (mLock) {
+ if (ar.exception == null) {
+ records = (List) ar.result;
+ } else {
+ if(DBG) logd("Cannot load ADN records");
+ if (records != null) {
+ records.clear();
+ }
+ }
+ notifyPending(ar);
+ }
+ break;
+ }
+ }
+
+ private void notifyPending(AsyncResult ar) {
+ if (ar.userObj == null) {
+ return;
+ }
+ AtomicBoolean status = (AtomicBoolean) ar.userObj;
+ status.set(true);
+ mLock.notifyAll();
+ }
+ };
+
+ public IccPhoneBookInterfaceManager(PhoneBase phone) {
+ this.phone = phone;
+ }
+
+ public void dispose() {
+ }
+
+ protected void publish() {
+ //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
+ ServiceManager.addService("simphonebook", this);
+ }
+
+ protected abstract void logd(String msg);
+
+ protected abstract void loge(String msg);
+
+ /**
+ * Replace oldAdn with newAdn in ADN-like record in EF
+ *
+ * getAdnRecordsInEf must be called at least once before this function,
+ * otherwise an error will be returned. Currently the email field
+ * if set in the ADN record is ignored.
+ * throws SecurityException if no WRITE_CONTACTS permission
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param oldTag adn tag to be replaced
+ * @param oldPhoneNumber adn number to be replaced
+ * Set both oldTag and oldPhoneNubmer to "" means to replace an
+ * empty record, aka, insert new record
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number ot be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @return true for success
+ */
+ public boolean
+ updateAdnRecordsInEfBySearch (int efid,
+ String oldTag, String oldPhoneNumber,
+ String newTag, String newPhoneNumber, String pin2) {
+
+
+ if (phone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires android.permission.WRITE_CONTACTS permission");
+ }
+
+
+ if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid +
+ " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" +
+ " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
+
+ efid = updateEfForIccType(efid);
+
+ synchronized(mLock) {
+ checkThread();
+ success = false;
+ AtomicBoolean status = new AtomicBoolean(false);
+ Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
+ AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
+ AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
+ adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
+ waitForResult(status);
+ }
+ return success;
+ }
+
+ /**
+ * Update an ADN-like EF record by record index
+ *
+ * This is useful for iteration the whole ADN file, such as write the whole
+ * phone book or erase/format the whole phonebook. Currently the email field
+ * if set in the ADN record is ignored.
+ * throws SecurityException if no WRITE_CONTACTS permission
+ *
+ * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+ * @param newTag adn tag to be stored
+ * @param newPhoneNumber adn number to be stored
+ * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * record with empty one, aka, delete old record
+ * @param index is 1-based adn record index to be updated
+ * @param pin2 required to update EF_FDN, otherwise must be null
+ * @return true for success
+ */
+ public boolean
+ updateAdnRecordsInEfByIndex(int efid, String newTag,
+ String newPhoneNumber, int index, String pin2) {
+
+ if (phone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires android.permission.WRITE_CONTACTS permission");
+ }
+
+ if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid +
+ " Index=" + index + " ==> " +
+ "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2);
+ synchronized(mLock) {
+ checkThread();
+ success = false;
+ AtomicBoolean status = new AtomicBoolean(false);
+ Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, status);
+ AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
+ adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
+ waitForResult(status);
+ }
+ return success;
+ }
+
+ /**
+ * Get the capacity of records in efid
+ *
+ * @param efid the EF id of a ADN-like ICC
+ * @return int[3] array
+ * recordSizes[0] is the single record length
+ * recordSizes[1] is the total length of the EF file
+ * recordSizes[2] is the number of records in the EF file
+ */
+ public abstract int[] getAdnRecordsSize(int efid);
+
+ /**
+ * Loads the AdnRecords in efid and returns them as a
+ * List of AdnRecords
+ *
+ * throws SecurityException if no READ_CONTACTS permission
+ *
+ * @param efid the EF id of a ADN-like ICC
+ * @return List of AdnRecord
+ */
+ public List getAdnRecordsInEf(int efid) {
+
+ if (phone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires android.permission.READ_CONTACTS permission");
+ }
+
+ efid = updateEfForIccType(efid);
+ if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
+
+ synchronized(mLock) {
+ checkThread();
+ AtomicBoolean status = new AtomicBoolean(false);
+ Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
+ adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
+ waitForResult(status);
+ }
+ return records;
+ }
+
+ protected void checkThread() {
+ if (!ALLOW_SIM_OP_IN_UI_THREAD) {
+ // Make sure this isn't the UI thread, since it will block
+ if (mBaseHandler.getLooper().equals(Looper.myLooper())) {
+ loge("query() called on the main UI thread!");
+ throw new IllegalStateException(
+ "You cannot call query on this provder from the main UI thread.");
+ }
+ }
+ }
+
+ protected void waitForResult(AtomicBoolean status) {
+ while (!status.get()) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ logd("interrupted while trying to update by search");
+ }
+ }
+ }
+
+ private int updateEfForIccType(int efid) {
+ // Check if we are trying to read ADN records
+ if (efid == IccConstants.EF_ADN) {
+ if (phone.getIccCard().isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) {
+ return IccConstants.EF_PBR;
+ }
+ }
+ return efid;
+ }
+}
+
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c0fc52e78eb413f8c90027b7d7197755b20d5f5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open 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.PackageManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * SimPhoneBookInterfaceManager to provide an inter-process communication to
+ * access ADN-like SIM records.
+ */
+public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub {
+ private IccPhoneBookInterfaceManager mIccPhoneBookInterfaceManager;
+
+ public IccPhoneBookInterfaceManagerProxy(IccPhoneBookInterfaceManager
+ iccPhoneBookInterfaceManager) {
+ mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
+ if(ServiceManager.getService("simphonebook") == null) {
+ ServiceManager.addService("simphonebook", this);
+ }
+ }
+
+ public void setmIccPhoneBookInterfaceManager(
+ IccPhoneBookInterfaceManager iccPhoneBookInterfaceManager) {
+ this.mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager;
+ }
+
+ public boolean
+ updateAdnRecordsInEfBySearch (int efid,
+ String oldTag, String oldPhoneNumber,
+ String newTag, String newPhoneNumber,
+ String pin2) throws android.os.RemoteException {
+ return mIccPhoneBookInterfaceManager.updateAdnRecordsInEfBySearch(
+ efid, oldTag, oldPhoneNumber, newTag, newPhoneNumber, pin2);
+ }
+
+ public boolean
+ updateAdnRecordsInEfByIndex(int efid, String newTag,
+ String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
+ return mIccPhoneBookInterfaceManager.updateAdnRecordsInEfByIndex(efid,
+ newTag, newPhoneNumber, index, pin2);
+ }
+
+ public int[] getAdnRecordsSize(int efid) throws android.os.RemoteException {
+ return mIccPhoneBookInterfaceManager.getAdnRecordsSize(efid);
+ }
+
+ public List getAdnRecordsInEf(int efid) throws android.os.RemoteException {
+ return mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..a66e19d15231bc53eb9c1123f56d6aafee74a534
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -0,0 +1,431 @@
+/*
+ * 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.content.ContentProvider;
+import android.content.UriMatcher;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+import com.android.internal.telephony.IccConstants;
+import com.android.internal.telephony.AdnRecord;
+import com.android.internal.telephony.IIccPhoneBook;
+
+
+/**
+ * {@hide}
+ */
+public class IccProvider extends ContentProvider {
+ private static final String TAG = "IccProvider";
+ private static final boolean DBG = false;
+
+
+ private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
+ "name",
+ "number",
+ "emails",
+ "_id"
+ };
+
+ private static final int ADN = 1;
+ private static final int FDN = 2;
+ private static final int SDN = 3;
+
+ private static final String STR_TAG = "tag";
+ private static final String STR_NUMBER = "number";
+ private static final String STR_EMAILS = "emails";
+ private static final String STR_PIN2 = "pin2";
+
+ private static final UriMatcher URL_MATCHER =
+ new UriMatcher(UriMatcher.NO_MATCH);
+
+ static {
+ URL_MATCHER.addURI("icc", "adn", ADN);
+ URL_MATCHER.addURI("icc", "fdn", FDN);
+ URL_MATCHER.addURI("icc", "sdn", SDN);
+ }
+
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projection, String selection,
+ String[] selectionArgs, String sort) {
+ switch (URL_MATCHER.match(url)) {
+ case ADN:
+ return loadFromEf(IccConstants.EF_ADN);
+
+ case FDN:
+ return loadFromEf(IccConstants.EF_FDN);
+
+ case SDN:
+ return loadFromEf(IccConstants.EF_SDN);
+
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ }
+
+ @Override
+ public String getType(Uri url) {
+ switch (URL_MATCHER.match(url)) {
+ case ADN:
+ case FDN:
+ case SDN:
+ return "vnd.android.cursor.dir/sim-contact";
+
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ Uri resultUri;
+ int efType;
+ String pin2 = null;
+
+ if (DBG) log("insert");
+
+ int match = URL_MATCHER.match(url);
+ switch (match) {
+ case ADN:
+ efType = IccConstants.EF_ADN;
+ break;
+
+ case FDN:
+ efType = IccConstants.EF_FDN;
+ pin2 = initialValues.getAsString("pin2");
+ break;
+
+ default:
+ throw new UnsupportedOperationException(
+ "Cannot insert into URL: " + url);
+ }
+
+ String tag = initialValues.getAsString("tag");
+ String number = initialValues.getAsString("number");
+ // TODO(): Read email instead of sending null.
+ boolean success = addIccRecordToEf(efType, tag, number, null, pin2);
+
+ if (!success) {
+ return null;
+ }
+
+ StringBuilder buf = new StringBuilder("content://icc/");
+ switch (match) {
+ case ADN:
+ buf.append("adn/");
+ break;
+
+ case FDN:
+ buf.append("fdn/");
+ break;
+ }
+
+ // TODO: we need to find out the rowId for the newly added record
+ buf.append(0);
+
+ resultUri = Uri.parse(buf.toString());
+
+ /*
+ // notify interested parties that an insertion happened
+ getContext().getContentResolver().notifyInsert(
+ resultUri, rowID, null);
+ */
+
+ return resultUri;
+ }
+
+ private String normalizeValue(String inVal) {
+ int len = inVal.length();
+ String retVal = inVal;
+
+ if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') {
+ retVal = inVal.substring(1, len-1);
+ }
+
+ return retVal;
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ int efType;
+
+ if (DBG) log("delete");
+
+ int match = URL_MATCHER.match(url);
+ switch (match) {
+ case ADN:
+ efType = IccConstants.EF_ADN;
+ break;
+
+ case FDN:
+ efType = IccConstants.EF_FDN;
+ break;
+
+ default:
+ throw new UnsupportedOperationException(
+ "Cannot insert into URL: " + url);
+ }
+
+ // parse where clause
+ String tag = null;
+ String number = null;
+ String[] emails = null;
+ String pin2 = null;
+
+ String[] tokens = where.split("AND");
+ int n = tokens.length;
+
+ while (--n >= 0) {
+ String param = tokens[n];
+ if (DBG) log("parsing '" + param + "'");
+
+ String[] pair = param.split("=");
+
+ if (pair.length != 2) {
+ Log.e(TAG, "resolve: bad whereClause parameter: " + param);
+ continue;
+ }
+
+ String key = pair[0].trim();
+ String val = pair[1].trim();
+
+ if (STR_TAG.equals(key)) {
+ tag = normalizeValue(val);
+ } else if (STR_NUMBER.equals(key)) {
+ number = normalizeValue(val);
+ } else if (STR_EMAILS.equals(key)) {
+ //TODO(): Email is null.
+ emails = null;
+ } else if (STR_PIN2.equals(key)) {
+ pin2 = normalizeValue(val);
+ }
+ }
+
+ if (TextUtils.isEmpty(number)) {
+ return 0;
+ }
+
+ if (efType == IccConstants.EF_FDN && TextUtils.isEmpty(pin2)) {
+ return 0;
+ }
+
+ boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2);
+ if (!success) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ int efType;
+ String pin2 = null;
+
+ if (DBG) log("update");
+
+ int match = URL_MATCHER.match(url);
+ switch (match) {
+ case ADN:
+ efType = IccConstants.EF_ADN;
+ break;
+
+ case FDN:
+ efType = IccConstants.EF_FDN;
+ pin2 = values.getAsString("pin2");
+ break;
+
+ default:
+ throw new UnsupportedOperationException(
+ "Cannot insert into URL: " + url);
+ }
+
+ String tag = values.getAsString("tag");
+ String number = values.getAsString("number");
+ String[] emails = null;
+ String newTag = values.getAsString("newTag");
+ String newNumber = values.getAsString("newNumber");
+ String[] newEmails = null;
+ // TODO(): Update for email.
+ boolean success = updateIccRecordInEf(efType, tag, number,
+ newTag, newNumber, pin2);
+
+ if (!success) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ private MatrixCursor loadFromEf(int efType) {
+ if (DBG) log("loadFromEf: efType=" + efType);
+
+ List adnRecords = null;
+ try {
+ IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
+ ServiceManager.getService("simphonebook"));
+ if (iccIpb != null) {
+ adnRecords = iccIpb.getAdnRecordsInEf(efType);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ } catch (SecurityException ex) {
+ if (DBG) log(ex.toString());
+ }
+
+ if (adnRecords != null) {
+ // Load the results
+ final int N = adnRecords.size();
+ final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, N);
+ if (DBG) log("adnRecords.size=" + N);
+ for (int i = 0; i < N ; i++) {
+ loadRecord(adnRecords.get(i), cursor, i);
+ }
+ return cursor;
+ } else {
+ // No results to load
+ Log.w(TAG, "Cannot load ADN records");
+ return new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES);
+ }
+ }
+
+ private boolean
+ addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) {
+ if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +
+ ", number=" + number + ", emails=" + emails);
+
+ boolean success = false;
+
+ // TODO: do we need to call getAdnRecordsInEf() before calling
+ // updateAdnRecordsInEfBySearch()? In any case, we will leave
+ // the UI level logic to fill that prereq if necessary. But
+ // hopefully, we can remove this requirement.
+
+ try {
+ IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
+ ServiceManager.getService("simphonebook"));
+ if (iccIpb != null) {
+ success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "",
+ name, number, pin2);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ } catch (SecurityException ex) {
+ if (DBG) log(ex.toString());
+ }
+ if (DBG) log("addIccRecordToEf: " + success);
+ return success;
+ }
+
+ private boolean
+ updateIccRecordInEf(int efType, String oldName, String oldNumber,
+ String newName, String newNumber, String pin2) {
+ if (DBG) log("updateIccRecordInEf: efType=" + efType +
+ ", oldname=" + oldName + ", oldnumber=" + oldNumber +
+ ", newname=" + newName + ", newnumber=" + newNumber);
+ boolean success = false;
+
+ try {
+ IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
+ ServiceManager.getService("simphonebook"));
+ if (iccIpb != null) {
+ success = iccIpb.updateAdnRecordsInEfBySearch(efType,
+ oldName, oldNumber, newName, newNumber, pin2);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ } catch (SecurityException ex) {
+ if (DBG) log(ex.toString());
+ }
+ if (DBG) log("updateIccRecordInEf: " + success);
+ return success;
+ }
+
+
+ private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
+ String pin2) {
+ if (DBG) log("deleteIccRecordFromEf: efType=" + efType +
+ ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2);
+
+ boolean success = false;
+
+ try {
+ IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
+ ServiceManager.getService("simphonebook"));
+ if (iccIpb != null) {
+ success = iccIpb.updateAdnRecordsInEfBySearch(efType,
+ name, number, "", "", pin2);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ } catch (SecurityException ex) {
+ if (DBG) log(ex.toString());
+ }
+ if (DBG) log("deleteIccRecordFromEf: " + success);
+ return success;
+ }
+
+ /**
+ * Loads an AdnRecord into a MatrixCursor. Must be called with mLock held.
+ *
+ * @param record the ADN record to load from
+ * @param cursor the cursor to receive the results
+ */
+ private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) {
+ if (!record.isEmpty()) {
+ Object[] contact = new Object[4];
+ String alphaTag = record.getAlphaTag();
+ String number = record.getNumber();
+
+ if (DBG) log("loadRecord: " + alphaTag + ", " + number + ",");
+ contact[0] = alphaTag;
+ contact[1] = number;
+
+ String[] emails = record.getEmails();
+ if (emails != null) {
+ StringBuilder emailString = new StringBuilder();
+ for (String email: emails) {
+ if (DBG) log("Adding email:" + email);
+ emailString.append(email);
+ emailString.append(",");
+ }
+ contact[2] = emailString.toString();
+ }
+ contact[3] = id;
+ cursor.addRow(contact);
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(TAG, "[IccProvider] " + msg);
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/IccRecords.java b/src/java/com/android/internal/telephony/IccRecords.java
new file mode 100644
index 0000000000000000000000000000000000000000..41c9d5ad1c2e86a6e610c3ee3ffafb40a68be4ee
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccRecords.java
@@ -0,0 +1,422 @@
+/*
+ * 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.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+
+import com.android.internal.telephony.gsm.UsimServiceTable;
+import com.android.internal.telephony.ims.IsimRecords;
+
+/**
+ * {@hide}
+ */
+public abstract class IccRecords extends Handler implements IccConstants {
+
+ protected static final boolean DBG = true;
+ // ***** Instance Variables
+ protected boolean mDestroyed = false; // set to true once this object needs to be disposed of
+ protected Context mContext;
+ protected CommandsInterface mCi;
+ protected IccFileHandler mFh;
+ protected IccCard mParentCard;
+
+ protected RegistrantList recordsLoadedRegistrants = new RegistrantList();
+ protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
+ protected RegistrantList mNewSmsRegistrants = new RegistrantList();
+ protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
+
+ protected int recordsToLoad; // number of pending load requests
+
+ protected AdnRecordCache adnCache;
+
+ // ***** Cached SIM State; cleared on channel close
+
+ protected boolean recordsRequested = false; // true if we've made requests for the sim records
+
+ public String iccid;
+ protected String msisdn = null; // My mobile number
+ protected String msisdnTag = null;
+ protected String voiceMailNum = null;
+ protected String voiceMailTag = null;
+ protected String newVoiceMailNum = null;
+ protected String newVoiceMailTag = null;
+ protected boolean isVoiceMailFixed = false;
+ protected int countVoiceMessages = 0;
+
+ protected int mncLength = UNINITIALIZED;
+ protected int mailboxIndex = 0; // 0 is no mailbox dailing number associated
+
+ protected String spn;
+
+ // ***** Constants
+
+ // Markers for mncLength
+ protected static final int UNINITIALIZED = -1;
+ protected static final int UNKNOWN = 0;
+
+ // Bitmasks for SPN display rules.
+ protected static final int SPN_RULE_SHOW_SPN = 0x01;
+ protected static final int SPN_RULE_SHOW_PLMN = 0x02;
+
+ // ***** Event Constants
+ protected static final int EVENT_SET_MSISDN_DONE = 30;
+ public static final int EVENT_MWI = 0;
+ public static final int EVENT_CFI = 1;
+ public static final int EVENT_SPN = 2;
+
+ public static final int EVENT_GET_ICC_RECORD_DONE = 100;
+
+ /**
+ * Generic ICC record loaded callback. Subclasses can call EF load methods on
+ * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
+ * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
+ * of this interface. The {@link #handleMessage} method in this class will print a
+ * log message using {@link #getEfName()} and decrement {@link #recordsToLoad}.
+ *
+ * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
+ * Otherwise, an error log message will be output by {@link #handleMessage} and
+ * {@link #onRecordLoaded} will not be called.
+ */
+ public interface IccRecordLoaded {
+ String getEfName();
+ void onRecordLoaded(AsyncResult ar);
+ }
+
+ // ***** Constructor
+ public IccRecords(IccCard card, Context c, CommandsInterface ci) {
+ mContext = c;
+ mCi = ci;
+ mFh = card.getIccFileHandler();
+ mParentCard = card;
+ }
+
+ /**
+ * Call when the IccRecords object is no longer going to be used.
+ */
+ public void dispose() {
+ mDestroyed = true;
+ mParentCard = null;
+ mFh = null;
+ mCi = null;
+ mContext = null;
+ }
+
+ protected abstract void onRadioOffOrNotAvailable();
+ public abstract void onReady();
+
+ //***** Public Methods
+ public AdnRecordCache getAdnCache() {
+ return adnCache;
+ }
+
+ public IccCard getIccCard() {
+ return mParentCard;
+ }
+
+ public void registerForRecordsLoaded(Handler h, int what, Object obj) {
+ if (mDestroyed) {
+ return;
+ }
+
+ Registrant r = new Registrant(h, what, obj);
+ recordsLoadedRegistrants.add(r);
+
+ if (recordsToLoad == 0 && recordsRequested == true) {
+ r.notifyRegistrant(new AsyncResult(null, null, null));
+ }
+ }
+ public void unregisterForRecordsLoaded(Handler h) {
+ recordsLoadedRegistrants.remove(h);
+ }
+
+ public void registerForRecordsEvents(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mRecordsEventsRegistrants.add(r);
+ }
+ public void unregisterForRecordsEvents(Handler h) {
+ mRecordsEventsRegistrants.remove(h);
+ }
+
+ public void registerForNewSms(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mNewSmsRegistrants.add(r);
+ }
+ public void unregisterForNewSms(Handler h) {
+ mNewSmsRegistrants.remove(h);
+ }
+
+ public void registerForNetworkSelectionModeAutomatic(
+ Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+ mNetworkSelectionModeAutomaticRegistrants.add(r);
+ }
+ public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
+ mNetworkSelectionModeAutomaticRegistrants.remove(h);
+ }
+
+ /**
+ * Get the International Mobile Subscriber ID (IMSI) on a SIM
+ * for GSM, UMTS and like networks. Default is null if IMSI is
+ * not supported or unavailable.
+ *
+ * @return null if SIM is not yet ready or unavailable
+ */
+ public String getIMSI() {
+ return null;
+ }
+
+ public String getMsisdnNumber() {
+ return msisdn;
+ }
+
+ /**
+ * Set subscriber number to SIM record
+ *
+ * The subscriber number is stored in EF_MSISDN (TS 51.011)
+ *
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
+ * @param number dailing nubmer (up to 20 digits)
+ * if the number starts with '+', then set to international TOA
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void setMsisdnNumber(String alphaTag, String number,
+ Message onComplete) {
+
+ msisdn = number;
+ msisdnTag = alphaTag;
+
+ if(DBG) log("Set MSISDN: " + msisdnTag +" " + msisdn);
+
+
+ AdnRecord adn = new AdnRecord(msisdnTag, msisdn);
+
+ new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
+ obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
+ }
+
+ public String getMsisdnAlphaTag() {
+ return msisdnTag;
+ }
+
+ public String getVoiceMailNumber() {
+ return voiceMailNum;
+ }
+
+ /**
+ * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41)
+ * @return null if SIM is not yet ready or no RUIM entry
+ */
+ public String getServiceProviderName() {
+ return spn;
+ }
+
+ /**
+ * Set voice mail number to SIM record
+ *
+ * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
+ * EF_MAILBOX_CPHS (CPHS 4.2)
+ *
+ * If EF_MBDN is available, store the voice mail number to EF_MBDN
+ *
+ * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
+ *
+ * So the voice mail number will be stored in both EFs if both are available
+ *
+ * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
+ *
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
+ * @param voiceNumber dailing nubmer (upto 20 digits)
+ * if the number is start with '+', then set to international TOA
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
+ Message onComplete);
+
+ public String getVoiceMailAlphaTag() {
+ return voiceMailTag;
+ }
+
+ /**
+ * Sets the SIM voice message waiting indicator records
+ * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
+ * @param countWaiting The number of messages waiting, if known. Use
+ * -1 to indicate that an unknown number of
+ * messages are waiting
+ */
+ public abstract void setVoiceMessageWaiting(int line, int countWaiting);
+
+ /** @return true if there are messages waiting, false otherwise. */
+ public boolean getVoiceMessageWaiting() {
+ return countVoiceMessages != 0;
+ }
+
+ /**
+ * Returns number of voice messages waiting, if available
+ * If not available (eg, on an older CPHS SIM) -1 is returned if
+ * getVoiceMessageWaiting() is true
+ */
+ public int getVoiceMessageCount() {
+ return countVoiceMessages;
+ }
+
+ /**
+ * Called by STK Service when REFRESH is received.
+ * @param fileChanged indicates whether any files changed
+ * @param fileList if non-null, a list of EF files that changed
+ */
+ public abstract void onRefresh(boolean fileChanged, int[] fileList);
+
+
+ public boolean getRecordsLoaded() {
+ if (recordsToLoad == 0 && recordsRequested == true) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //***** Overridden from Handler
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_GET_ICC_RECORD_DONE:
+ try {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
+ if (DBG) log(recordLoaded.getEfName() + " LOADED");
+
+ if (ar.exception != null) {
+ loge("Record Load Exception: " + ar.exception);
+ } else {
+ recordLoaded.onRecordLoaded(ar);
+ }
+ }catch (RuntimeException exc) {
+ // I don't want these exceptions to be fatal
+ loge("Exception parsing SIM record: " + exc);
+ } finally {
+ // Count up record load responses even if they are fails
+ onRecordLoaded();
+ }
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ protected abstract void onRecordLoaded();
+
+ protected abstract void onAllRecordsLoaded();
+
+ /**
+ * Returns the SpnDisplayRule based on settings on the SIM and the
+ * specified plmn (currently-registered PLMN). See TS 22.101 Annex A
+ * and TS 51.011 10.3.11 for details.
+ *
+ * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
+ * Generally used for GSM/UMTS and the like SIMs.
+ */
+ public abstract int getDisplayRule(String plmn);
+
+ /**
+ * Return true if "Restriction of menu options for manual PLMN selection"
+ * bit is set or EF_CSP data is unavailable, return false otherwise.
+ * Generally used for GSM/UMTS and the like SIMs.
+ */
+ public boolean isCspPlmnEnabled() {
+ return false;
+ }
+
+ /**
+ * Returns the 5 or 6 digit MCC/MNC of the operator that
+ * provided the SIM card. Returns null of SIM is not yet ready
+ * or is not valid for the type of IccCard. Generally used for
+ * GSM/UMTS and the like SIMS
+ */
+ public String getOperatorNumeric() {
+ return null;
+ }
+
+ /**
+ * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
+ *
+ * @return true if enabled
+ */
+ public boolean getVoiceCallForwardingFlag() {
+ return false;
+ }
+
+ /**
+ * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
+ *
+ * @param line to enable/disable
+ * @param enable
+ */
+ public void setVoiceCallForwardingFlag(int line, boolean enable) {
+ }
+
+ /**
+ * Indicates wether SIM is in provisioned state or not.
+ * Overridden only if SIM can be dynamically provisioned via OTA.
+ *
+ * @return true if provisioned
+ */
+ public boolean isProvisioned () {
+ return true;
+ }
+
+ /**
+ * Write string to log file
+ *
+ * @param s is the string to write
+ */
+ protected abstract void log(String s);
+
+ /**
+ * Write error string to log file.
+ *
+ * @param s is the string to write
+ */
+ protected abstract void loge(String s);
+
+ /**
+ * Return an interface to retrieve the ISIM records for IMS, if available.
+ * @return the interface to retrieve the ISIM records, or null if not supported
+ */
+ public IsimRecords getIsimRecords() {
+ return null;
+ }
+
+ public UsimServiceTable getUsimServiceTable() {
+ return null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccRefreshResponse.java b/src/java/com/android/internal/telephony/IccRefreshResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..680670311cf3b30883417a04382b42e07a76d038
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccRefreshResponse.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+/**
+ * See also RIL_SimRefresh in include/telephony/ril.h
+ *
+ * {@hide}
+ */
+
+public class IccRefreshResponse {
+
+ public static final int REFRESH_RESULT_FILE_UPDATE = 0; /* Single file was updated */
+ public static final int REFRESH_RESULT_INIT = 1; /* The Icc has been initialized */
+ public static final int REFRESH_RESULT_RESET = 2; /* The Icc was reset */
+
+ public int refreshResult; /* Sim Refresh result */
+ public int efId; /* EFID */
+ public String aid; /* null terminated string, e.g.,
+ from 0xA0, 0x00 -> 0x41,
+ 0x30, 0x30, 0x30 */
+ /* Example: a0000000871002f310ffff89080000ff */
+
+ @Override
+ public String toString() {
+ return "{" + refreshResult + ", " + aid +", " + efId + "}";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccServiceTable.java b/src/java/com/android/internal/telephony/IccServiceTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed74a1175b6bc5b861c382868f85763307ccaf46
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccServiceTable.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+
+/**
+ * Wrapper class for an ICC EF containing a bit field of enabled services.
+ */
+public abstract class IccServiceTable {
+ protected final byte[] mServiceTable;
+
+ protected IccServiceTable(byte[] table) {
+ mServiceTable = table;
+ }
+
+ // Get the class name to use for log strings
+ protected abstract String getTag();
+
+ // Get the array of enums to use for toString
+ protected abstract Object[] getValues();
+
+ /**
+ * Returns if the specified service is available.
+ * @param service the service number as a zero-based offset (the enum ordinal)
+ * @return true if the service is available; false otherwise
+ */
+ protected boolean isAvailable(int service) {
+ int offset = service / 8;
+ if (offset >= mServiceTable.length) {
+ // Note: Enums are zero-based, but the TS service numbering is one-based
+ Log.e(getTag(), "isAvailable for service " + (service + 1) + " fails, max service is " +
+ (mServiceTable.length * 8));
+ return false;
+ }
+ int bit = service % 8;
+ return (mServiceTable[offset] & (1 << bit)) != 0;
+ }
+
+ public String toString() {
+ Object[] values = getValues();
+ int numBytes = mServiceTable.length;
+ StringBuilder builder = new StringBuilder(getTag()).append('[')
+ .append(numBytes * 8).append("]={ ");
+
+ boolean addComma = false;
+ for (int i = 0; i < numBytes; i++) {
+ byte currentByte = mServiceTable[i];
+ for (int bit = 0; bit < 8; bit++) {
+ if ((currentByte & (1 << bit)) != 0) {
+ if (addComma) {
+ builder.append(", ");
+ } else {
+ addComma = true;
+ }
+ int ordinal = (i * 8) + bit;
+ if (ordinal < values.length) {
+ builder.append(values[ordinal]);
+ } else {
+ builder.append('#').append(ordinal + 1); // service number (one-based)
+ }
+ }
+ }
+ }
+ return builder.append(" }").toString();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fef6de70a25ba52c7efe17127ab7008968afaeb
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
+
+/**
+ * IccSmsInterfaceManager to provide an inter-process communication to
+ * access Sms in Icc.
+ */
+public abstract class IccSmsInterfaceManager extends ISms.Stub {
+ protected PhoneBase mPhone;
+ protected Context mContext;
+ protected SMSDispatcher mDispatcher;
+
+ protected IccSmsInterfaceManager(PhoneBase phone){
+ mPhone = phone;
+ mContext = phone.getContext();
+ }
+
+ protected void enforceReceiveAndSend(String message) {
+ mContext.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS", message);
+ mContext.enforceCallingPermission(
+ "android.permission.SEND_SMS", message);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ public void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingPermission(
+ "android.permission.SEND_SMS",
+ "Sending SMS message");
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
+ destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" +
+ sentIntent + " deliveryIntent=" + deliveryIntent);
+ }
+ mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ public void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingPermission(
+ "android.permission.SEND_SMS",
+ "Sending SMS message");
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
+ " text='"+ text + "' sentIntent=" +
+ sentIntent + " deliveryIntent=" + deliveryIntent);
+ }
+ mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Send a multi-part text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param parts an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ */
+ public void sendMultipartText(String destAddr, String scAddr, List parts,
+ List sentIntents, List deliveryIntents) {
+ mPhone.getContext().enforceCallingPermission(
+ "android.permission.SEND_SMS",
+ "Sending SMS message");
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ int i = 0;
+ for (String part : parts) {
+ log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
+ ", part[" + (i++) + "]=" + part);
+ }
+ }
+ mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList) parts,
+ (ArrayList) sentIntents, (ArrayList) deliveryIntents);
+ }
+
+ /**
+ * create SmsRawData lists from all sms record byte[]
+ * Use null to indicate "free" record
+ *
+ * @param messages List of message records from EF_SMS.
+ * @return SmsRawData list of all in-used records
+ */
+ protected ArrayList buildValidRawData(ArrayList messages) {
+ int count = messages.size();
+ ArrayList ret;
+
+ ret = new ArrayList(count);
+
+ for (int i = 0; i < count; i++) {
+ byte[] ba = messages.get(i);
+ if (ba[0] == STATUS_ON_ICC_FREE) {
+ ret.add(null);
+ } else {
+ ret.add(new SmsRawData(messages.get(i)));
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Generates an EF_SMS record from status and raw PDU.
+ *
+ * @param status Message status. See TS 51.011 10.5.3.
+ * @param pdu Raw message PDU.
+ * @return byte array for the record.
+ */
+ protected byte[] makeSmsRecordData(int status, byte[] pdu) {
+ byte[] data = new byte[IccConstants.SMS_RECORD_LENGTH];
+
+ // Status bits for this record. See TS 51.011 10.5.3
+ data[0] = (byte)(status & 7);
+
+ System.arraycopy(pdu, 0, data, 1, pdu.length);
+
+ // Pad out with 0xFF's.
+ for (int j = pdu.length+1; j < IccConstants.SMS_RECORD_LENGTH; j++) {
+ data[j] = -1;
+ }
+
+ return data;
+ }
+
+ protected abstract void log(String msg);
+
+}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
new file mode 100644
index 0000000000000000000000000000000000000000..54de508c04dee0bc49ee507fa4f241dc76af2237
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.PendingIntent;
+import android.os.ServiceManager;
+
+import java.util.List;
+
+public class IccSmsInterfaceManagerProxy extends ISms.Stub {
+ private IccSmsInterfaceManager mIccSmsInterfaceManager;
+
+ public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager
+ iccSmsInterfaceManager) {
+ this.mIccSmsInterfaceManager = iccSmsInterfaceManager;
+ if(ServiceManager.getService("isms") == null) {
+ ServiceManager.addService("isms", this);
+ }
+ }
+
+ public void setmIccSmsInterfaceManager(IccSmsInterfaceManager iccSmsInterfaceManager) {
+ this.mIccSmsInterfaceManager = iccSmsInterfaceManager;
+ }
+
+ public boolean
+ updateMessageOnIccEf(int index, int status, byte[] pdu) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.updateMessageOnIccEf(index, status, pdu);
+ }
+
+ public boolean copyMessageToIccEf(int status, byte[] pdu,
+ byte[] smsc) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.copyMessageToIccEf(status, pdu, smsc);
+ }
+
+ public List getAllMessagesFromIccEf() throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.getAllMessagesFromIccEf();
+ }
+
+ public void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mIccSmsInterfaceManager.sendData(destAddr, scAddr, destPort, data,
+ sentIntent, deliveryIntent);
+ }
+
+ public void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
+ }
+
+ public void sendMultipartText(String destAddr, String scAddr,
+ List parts, List sentIntents,
+ List deliveryIntents) throws android.os.RemoteException {
+ mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,
+ parts, sentIntents, deliveryIntents);
+ }
+
+ public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier);
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
+ }
+
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.enableCellBroadcastRange(startMessageId, endMessageId);
+ }
+
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.disableCellBroadcastRange(startMessageId, endMessageId);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccUtils.java b/src/java/com/android/internal/telephony/IccUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..a966f7694f09eaba7d720e7617726839ffa59bde
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccUtils.java
@@ -0,0 +1,530 @@
+/*
+ * 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.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.Log;
+
+import com.android.internal.telephony.GsmAlphabet;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * Various methods, useful for dealing with SIM data.
+ */
+public class IccUtils {
+ static final String LOG_TAG="IccUtils";
+
+ /**
+ * Many fields in GSM SIM's are stored as nibble-swizzled BCD
+ *
+ * Assumes left-justified field that may be padded right with 0xf
+ * values.
+ *
+ * Stops on invalid BCD value, returning string so far
+ */
+ public static String
+ bcdToString(byte[] data, int offset, int length) {
+ StringBuilder ret = new StringBuilder(length*2);
+
+ for (int i = offset ; i < offset + length ; i++) {
+ byte b;
+ int v;
+
+ v = data[i] & 0xf;
+ if (v > 9) break;
+ ret.append((char)('0' + v));
+
+ v = (data[i] >> 4) & 0xf;
+ // Some PLMNs have 'f' as high nibble, ignore it
+ if (v == 0xf) continue;
+ if (v > 9) break;
+ ret.append((char)('0' + v));
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Decode cdma byte into String.
+ */
+ public static String
+ cdmaBcdToString(byte[] data, int offset, int length) {
+ StringBuilder ret = new StringBuilder(length);
+
+ int count = 0;
+ for (int i = offset; count < length; i++) {
+ int v;
+ v = data[i] & 0xf;
+ if (v > 9) v = 0;
+ ret.append((char)('0' + v));
+
+ if (++count == length) break;
+
+ v = (data[i] >> 4) & 0xf;
+ if (v > 9) v = 0;
+ ret.append((char)('0' + v));
+ ++count;
+ }
+ return ret.toString();
+ }
+
+ /**
+ * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
+ *
+ * In GSM land, the least significant BCD digit is stored in the most
+ * significant nibble.
+ *
+ * Out-of-range digits are treated as 0 for the sake of the time stamp,
+ * because of this:
+ *
+ * TS 23.040 section 9.2.3.11
+ * "if the MS receives a non-integer value in the SCTS, it shall
+ * assume the digit is set to 0 but shall store the entire field
+ * exactly as received"
+ */
+ public static int
+ gsmBcdByteToInt(byte b) {
+ int ret = 0;
+
+ // treat out-of-range BCD values as 0
+ if ((b & 0xf0) <= 0x90) {
+ ret = (b >> 4) & 0xf;
+ }
+
+ if ((b & 0x0f) <= 0x09) {
+ ret += (b & 0xf) * 10;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Decodes a CDMA style BCD byte like {@link gsmBcdByteToInt}, but
+ * opposite nibble format. The least significant BCD digit
+ * is in the least significant nibble and the most significant
+ * is in the most significant nibble.
+ */
+ public static int
+ cdmaBcdByteToInt(byte b) {
+ int ret = 0;
+
+ // treat out-of-range BCD values as 0
+ if ((b & 0xf0) <= 0x90) {
+ ret = ((b >> 4) & 0xf) * 10;
+ }
+
+ if ((b & 0x0f) <= 0x09) {
+ ret += (b & 0xf);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Decodes a string field that's formatted like the EF[ADN] alpha
+ * identifier
+ *
+ * From TS 51.011 10.5.1:
+ * Coding:
+ * this alpha tagging shall use either
+ * - the SMS default 7 bit coded alphabet as defined in
+ * TS 23.038 [12] with bit 8 set to 0. The alpha identifier
+ * shall be left justified. Unused bytes shall be set to 'FF'; or
+ * - one of the UCS2 coded options as defined in annex B.
+ *
+ * Annex B from TS 11.11 V8.13.0:
+ * 1) If the first octet in the alpha string is '80', then the
+ * remaining octets are 16 bit UCS2 characters ...
+ * 2) if the first octet in the alpha string is '81', then the
+ * second octet contains a value indicating the number of
+ * characters in the string, and the third octet contains an
+ * 8 bit number which defines bits 15 to 8 of a 16 bit
+ * base pointer, where bit 16 is set to zero and bits 7 to 1
+ * are also set to zero. These sixteen bits constitute a
+ * base pointer to a "half page" in the UCS2 code space, to be
+ * used with some or all of the remaining octets in the string.
+ * The fourth and subsequent octets contain codings as follows:
+ * If bit 8 of the octet is set to zero, the remaining 7 bits
+ * of the octet contain a GSM Default Alphabet character,
+ * whereas if bit 8 of the octet is set to one, then the
+ * remaining seven bits are an offset value added to the
+ * 16 bit base pointer defined earlier...
+ * 3) If the first octet of the alpha string is set to '82', then
+ * the second octet contains a value indicating the number of
+ * characters in the string, and the third and fourth octets
+ * contain a 16 bit number which defines the complete 16 bit
+ * base pointer to a "half page" in the UCS2 code space...
+ */
+ public static String
+ adnStringFieldToString(byte[] data, int offset, int length) {
+ if (length == 0) {
+ return "";
+ }
+ if (length >= 1) {
+ if (data[offset] == (byte) 0x80) {
+ int ucslen = (length - 1) / 2;
+ String ret = null;
+
+ try {
+ ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
+ } catch (UnsupportedEncodingException ex) {
+ Log.e(LOG_TAG, "implausible UnsupportedEncodingException",
+ ex);
+ }
+
+ if (ret != null) {
+ // trim off trailing FFFF characters
+
+ ucslen = ret.length();
+ while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
+ ucslen--;
+
+ return ret.substring(0, ucslen);
+ }
+ }
+ }
+
+ boolean isucs2 = false;
+ char base = '\0';
+ int len = 0;
+
+ if (length >= 3 && data[offset] == (byte) 0x81) {
+ len = data[offset + 1] & 0xFF;
+ if (len > length - 3)
+ len = length - 3;
+
+ base = (char) ((data[offset + 2] & 0xFF) << 7);
+ offset += 3;
+ isucs2 = true;
+ } else if (length >= 4 && data[offset] == (byte) 0x82) {
+ len = data[offset + 1] & 0xFF;
+ if (len > length - 4)
+ len = length - 4;
+
+ base = (char) (((data[offset + 2] & 0xFF) << 8) |
+ (data[offset + 3] & 0xFF));
+ offset += 4;
+ isucs2 = true;
+ }
+
+ if (isucs2) {
+ StringBuilder ret = new StringBuilder();
+
+ while (len > 0) {
+ // UCS2 subset case
+
+ if (data[offset] < 0) {
+ ret.append((char) (base + (data[offset] & 0x7F)));
+ offset++;
+ len--;
+ }
+
+ // GSM character set case
+
+ int count = 0;
+ while (count < len && data[offset + count] >= 0)
+ count++;
+
+ ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
+ offset, count));
+
+ offset += count;
+ len -= count;
+ }
+
+ return ret.toString();
+ }
+
+ Resources resource = Resources.getSystem();
+ String defaultCharset = "";
+ try {
+ defaultCharset =
+ resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
+ } catch (NotFoundException e) {
+ // Ignore Exception and defaultCharset is set to a empty string.
+ }
+ return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
+ }
+
+ static int
+ hexCharToInt(char c) {
+ if (c >= '0' && c <= '9') return (c - '0');
+ if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+
+ throw new RuntimeException ("invalid hex char '" + c + "'");
+ }
+
+ /**
+ * Converts a hex String to a byte array.
+ *
+ * @param s A string of hexadecimal characters, must be an even number of
+ * chars long
+ *
+ * @return byte array representation
+ *
+ * @throws RuntimeException on invalid format
+ */
+ public static byte[]
+ hexStringToBytes(String s) {
+ byte[] ret;
+
+ if (s == null) return null;
+
+ int sz = s.length();
+
+ ret = new byte[sz/2];
+
+ for (int i=0 ; i > 4);
+
+ ret.append("0123456789abcdef".charAt(b));
+
+ b = 0x0f & bytes[i];
+
+ ret.append("0123456789abcdef".charAt(b));
+ }
+
+ return ret.toString();
+ }
+
+
+ /**
+ * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string
+ * "offset" points to "octet 3", the coding scheme byte
+ * empty string returned on decode error
+ */
+ public static String
+ networkNameToString(byte[] data, int offset, int length) {
+ String ret;
+
+ if ((data[offset] & 0x80) != 0x80 || length < 1) {
+ return "";
+ }
+
+ switch ((data[offset] >>> 4) & 0x7) {
+ case 0:
+ // SMS character set
+ int countSeptets;
+ int unusedBits = data[offset] & 7;
+ countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
+ ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
+ break;
+ case 1:
+ // UCS2
+ try {
+ ret = new String(data,
+ offset + 1, length - 1, "utf-16");
+ } catch (UnsupportedEncodingException ex) {
+ ret = "";
+ Log.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
+ }
+ break;
+
+ // unsupported encoding
+ default:
+ ret = "";
+ break;
+ }
+
+ // "Add CI"
+ // "The MS should add the letters for the Country's Initials and
+ // a separator (e.g. a space) to the text string"
+
+ if ((data[offset] & 0x40) != 0) {
+ // FIXME(mkf) add country initials here
+
+ }
+
+ return ret;
+ }
+
+ /**
+ * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
+ * @param data The raw data
+ * @param length The length of image body
+ * @return The bitmap
+ */
+ public static Bitmap parseToBnW(byte[] data, int length){
+ int valueIndex = 0;
+ int width = data[valueIndex++] & 0xFF;
+ int height = data[valueIndex++] & 0xFF;
+ int numOfPixels = width*height;
+
+ int[] pixels = new int[numOfPixels];
+
+ int pixelIndex = 0;
+ int bitIndex = 7;
+ byte currentByte = 0x00;
+ while (pixelIndex < numOfPixels) {
+ // reassign data and index for every byte (8 bits).
+ if (pixelIndex % 8 == 0) {
+ currentByte = data[valueIndex++];
+ bitIndex = 7;
+ }
+ pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
+ };
+
+ if (pixelIndex != numOfPixels) {
+ Log.e(LOG_TAG, "parse end and size error");
+ }
+ return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ private static int bitToRGB(int bit){
+ if(bit == 1){
+ return Color.WHITE;
+ } else {
+ return Color.BLACK;
+ }
+ }
+
+ /**
+ * a TS 131.102 image instance of code scheme '11' into color Bitmap
+ *
+ * @param data The raw data
+ * @param length the length of image body
+ * @param transparency with or without transparency
+ * @return The color bitmap
+ */
+ public static Bitmap parseToRGB(byte[] data, int length,
+ boolean transparency) {
+ int valueIndex = 0;
+ int width = data[valueIndex++] & 0xFF;
+ int height = data[valueIndex++] & 0xFF;
+ int bits = data[valueIndex++] & 0xFF;
+ int colorNumber = data[valueIndex++] & 0xFF;
+ int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
+ | (data[valueIndex++] & 0xFF);
+
+ int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
+ if (true == transparency) {
+ colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
+ }
+
+ int[] resultArray = null;
+ if (0 == (8 % bits)) {
+ resultArray = mapTo2OrderBitColor(data, valueIndex,
+ (width * height), colorIndexArray, bits);
+ } else {
+ resultArray = mapToNon2OrderBitColor(data, valueIndex,
+ (width * height), colorIndexArray, bits);
+ }
+
+ return Bitmap.createBitmap(resultArray, width, height,
+ Bitmap.Config.RGB_565);
+ }
+
+ private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
+ int length, int[] colorArray, int bits) {
+ if (0 != (8 % bits)) {
+ Log.e(LOG_TAG, "not event number of color");
+ return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
+ bits);
+ }
+
+ int mask = 0x01;
+ switch (bits) {
+ case 1:
+ mask = 0x01;
+ break;
+ case 2:
+ mask = 0x03;
+ break;
+ case 4:
+ mask = 0x0F;
+ break;
+ case 8:
+ mask = 0xFF;
+ break;
+ }
+
+ int[] resultArray = new int[length];
+ int resultIndex = 0;
+ int run = 8 / bits;
+ while (resultIndex < length) {
+ byte tempByte = data[valueIndex++];
+ for (int runIndex = 0; runIndex < run; ++runIndex) {
+ int offset = run - runIndex - 1;
+ resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
+ & mask];
+ }
+ }
+ return resultArray;
+ }
+
+ private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
+ int length, int[] colorArray, int bits) {
+ if (0 == (8 % bits)) {
+ Log.e(LOG_TAG, "not odd number of color");
+ return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
+ bits);
+ }
+
+ int[] resultArray = new int[length];
+ // TODO fix me:
+ return resultArray;
+ }
+
+ private static int[] getCLUT(byte[] rawData, int offset, int number) {
+ if (null == rawData) {
+ return null;
+ }
+
+ int[] result = new int[number];
+ int endIndex = offset + (number * 3); // 1 color use 3 bytes
+ int valueIndex = offset;
+ int colorIndex = 0;
+ int alpha = 0xff << 24;
+ do {
+ result[colorIndex++] = alpha
+ | ((rawData[valueIndex++] & 0xFF) << 16)
+ | ((rawData[valueIndex++] & 0xFF) << 8)
+ | ((rawData[valueIndex++] & 0xFF));
+ } while (valueIndex < endIndex);
+ return result;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccVmFixedException.java b/src/java/com/android/internal/telephony/IccVmFixedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a75496ff69a19470c4988645d4e6fc8fb36f1fb5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccVmFixedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open 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;
+
+/**
+ * {@hide}
+ */
+public final class IccVmFixedException extends IccException {
+ IccVmFixedException()
+ {
+
+ }
+
+ public IccVmFixedException(String s)
+ {
+ super(s);
+ }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/IccVmNotSupportedException.java b/src/java/com/android/internal/telephony/IccVmNotSupportedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c9d12689acd5bc7c7692d12929e94da8356aef6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IccVmNotSupportedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open 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;
+
+/**
+ * {@hide}
+ */
+public final class IccVmNotSupportedException extends IccException {
+ IccVmNotSupportedException()
+ {
+
+ }
+
+ public IccVmNotSupportedException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IntRangeManager.java b/src/java/com/android/internal/telephony/IntRangeManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc7774d66c3ed1df632e9b92924437da35da34e1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IntRangeManager.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Clients can enable reception of SMS-CB messages for specific ranges of
+ * message identifiers (channels). This class keeps track of the currently
+ * enabled message identifiers and calls abstract methods to update the
+ * radio when the range of enabled message identifiers changes.
+ *
+ * An update is a call to {@link #startUpdate} followed by zero or more
+ * calls to {@link #addRange} followed by a call to {@link #finishUpdate}.
+ * Calls to {@link #enableRange} and {@link #disableRange} will perform
+ * an incremental update operation if the enabled ranges have changed.
+ * A full update operation (i.e. after a radio reset) can be performed
+ * by a call to {@link #updateRanges}.
+ *
+ * Clients are identified by String (the name associated with the User ID
+ * of the caller) so that a call to remove a range can be mapped to the
+ * client that enabled that range (or else rejected).
+ */
+public abstract class IntRangeManager {
+
+ /**
+ * Initial capacity for IntRange clients array list. There will be
+ * few cell broadcast listeners on a typical device, so this can be small.
+ */
+ private static final int INITIAL_CLIENTS_ARRAY_SIZE = 4;
+
+ /**
+ * One or more clients forming the continuous range [startId, endId].
+ * When a client is added, the IntRange may merge with one or more
+ * adjacent IntRanges to form a single combined IntRange.
+ *
When a client is removed, the IntRange may divide into several
+ * non-contiguous IntRanges.
+ */
+ private class IntRange {
+ int startId;
+ int endId;
+ // sorted by earliest start id
+ final ArrayList clients;
+
+ /**
+ * Create a new IntRange with a single client.
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting the enabled range
+ */
+ IntRange(int startId, int endId, String client) {
+ this.startId = startId;
+ this.endId = endId;
+ clients = new ArrayList(INITIAL_CLIENTS_ARRAY_SIZE);
+ clients.add(new ClientRange(startId, endId, client));
+ }
+
+ /**
+ * Create a new IntRange for an existing ClientRange.
+ * @param clientRange the initial ClientRange to add
+ */
+ IntRange(ClientRange clientRange) {
+ startId = clientRange.startId;
+ endId = clientRange.endId;
+ clients = new ArrayList(INITIAL_CLIENTS_ARRAY_SIZE);
+ clients.add(clientRange);
+ }
+
+ /**
+ * Create a new IntRange from an existing IntRange. This is used for
+ * removing a ClientRange, because new IntRanges may need to be created
+ * for any gaps that open up after the ClientRange is removed. A copy
+ * is made of the elements of the original IntRange preceding the element
+ * that is being removed. The following elements will be added to this
+ * IntRange or to a new IntRange when a gap is found.
+ * @param intRange the original IntRange to copy elements from
+ * @param numElements the number of elements to copy from the original
+ */
+ IntRange(IntRange intRange, int numElements) {
+ this.startId = intRange.startId;
+ this.endId = intRange.endId;
+ this.clients = new ArrayList(intRange.clients.size());
+ for (int i=0; i < numElements; i++) {
+ this.clients.add(intRange.clients.get(i));
+ }
+ }
+
+ /**
+ * Insert new ClientRange in order by start id.
+ * If the new ClientRange is known to be sorted before or after the
+ * existing ClientRanges, or at a particular index, it can be added
+ * to the clients array list directly, instead of via this method.
+ *
Note that this can be changed from linear to binary search if the
+ * number of clients grows large enough that it would make a difference.
+ * @param range the new ClientRange to insert
+ */
+ void insert(ClientRange range) {
+ int len = clients.size();
+ for (int i=0; i < len; i++) {
+ ClientRange nextRange = clients.get(i);
+ if (range.startId <= nextRange.startId) {
+ // ignore duplicate ranges from the same client
+ if (!range.equals(nextRange)) {
+ clients.add(i, range);
+ }
+ return;
+ }
+ }
+ clients.add(range); // append to end of list
+ }
+ }
+
+ /**
+ * The message id range for a single client.
+ */
+ private class ClientRange {
+ final int startId;
+ final int endId;
+ final String client;
+
+ ClientRange(int startId, int endId, String client) {
+ this.startId = startId;
+ this.endId = endId;
+ this.client = client;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof ClientRange) {
+ ClientRange other = (ClientRange) o;
+ return startId == other.startId &&
+ endId == other.endId &&
+ client.equals(other.client);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return (startId * 31 + endId) * 31 + client.hashCode();
+ }
+ }
+
+ /**
+ * List of integer ranges, one per client, sorted by start id.
+ */
+ private ArrayList mRanges = new ArrayList();
+
+ protected IntRangeManager() {}
+
+ /**
+ * Enable a range for the specified client and update ranges
+ * if necessary. If {@link #finishUpdate} returns failure,
+ * false is returned and the range is not added.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting the enabled range
+ * @return true if successful, false otherwise
+ */
+ public synchronized boolean enableRange(int startId, int endId, String client) {
+ int len = mRanges.size();
+
+ // empty range list: add the initial IntRange
+ if (len == 0) {
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+
+ for (int startIndex = 0; startIndex < len; startIndex++) {
+ IntRange range = mRanges.get(startIndex);
+ if (startId < range.startId) {
+ // test if new range completely precedes this range
+ // note that [1, 4] and [5, 6] coalesce to [1, 6]
+ if ((endId + 1) < range.startId) {
+ // insert new int range before previous first range
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(startIndex, new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else if (endId <= range.endId) {
+ // extend the start of this range
+ if (tryAddSingleRange(startId, range.startId - 1, true)) {
+ range.startId = startId;
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else {
+ // find last range that can coalesce into the new combined range
+ for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
+ IntRange endRange = mRanges.get(endIndex);
+ if ((endId + 1) < endRange.startId) {
+ // try to add entire new range
+ if (tryAddSingleRange(startId, endId, true)) {
+ range.startId = startId;
+ range.endId = endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else if (endId <= endRange.endId) {
+ // add range from start id to start of last overlapping range,
+ // values from endRange.startId to endId are already enabled
+ if (tryAddSingleRange(startId, endRange.startId - 1, true)) {
+ range.startId = startId;
+ range.endId = endRange.endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i <= endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ }
+
+ // endId extends past all existing IntRanges: combine them all together
+ if (tryAddSingleRange(startId, endId, true)) {
+ range.startId = startId;
+ range.endId = endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to len-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < len; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ } else if ((startId + 1) <= range.endId) {
+ if (endId <= range.endId) {
+ // completely contained in existing range; no radio changes
+ range.insert(new ClientRange(startId, endId, client));
+ return true;
+ } else {
+ // find last range that can coalesce into the new combined range
+ int endIndex = startIndex;
+ for (int testIndex = startIndex+1; testIndex < len; testIndex++) {
+ IntRange testRange = mRanges.get(testIndex);
+ if ((endId + 1) < testRange.startId) {
+ break;
+ } else {
+ endIndex = testIndex;
+ }
+ }
+ // no adjacent IntRanges to combine
+ if (endIndex == startIndex) {
+ // add range from range.endId+1 to endId,
+ // values from startId to range.endId are already enabled
+ if (tryAddSingleRange(range.endId + 1, endId, true)) {
+ range.endId = endId;
+ range.insert(new ClientRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ // get last range to coalesce into start range
+ IntRange endRange = mRanges.get(endIndex);
+ // Values from startId to range.endId have already been enabled.
+ // if endId > endRange.endId, then enable range from range.endId+1 to endId,
+ // else enable range from range.endId+1 to endRange.startId-1, because
+ // values from endRange.startId to endId have already been added.
+ int newRangeEndId = (endId <= endRange.endId) ? endRange.startId - 1 : endId;
+ if (tryAddSingleRange(range.endId + 1, newRangeEndId, true)) {
+ range.endId = endId;
+ // insert new ClientRange in place
+ range.insert(new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1 (joinIndex).
+ // i is the index if no elements had been removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ }
+ }
+
+ // append new range after existing IntRanges
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+
+ /**
+ * Disable a range for the specified client and update ranges
+ * if necessary. If {@link #finishUpdate} returns failure,
+ * false is returned and the range is not removed.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting to disable the range
+ * @return true if successful, false otherwise
+ */
+ public synchronized boolean disableRange(int startId, int endId, String client) {
+ int len = mRanges.size();
+
+ for (int i=0; i < len; i++) {
+ IntRange range = mRanges.get(i);
+ if (startId < range.startId) {
+ return false; // not found
+ } else if (endId <= range.endId) {
+ // found the IntRange that encloses the client range, if any
+ // search for it in the clients list
+ ArrayList clients = range.clients;
+
+ // handle common case of IntRange containing one ClientRange
+ int crLength = clients.size();
+ if (crLength == 1) {
+ ClientRange cr = clients.get(0);
+ if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+ // disable range in radio then remove the entire IntRange
+ if (tryAddSingleRange(startId, endId, false)) {
+ mRanges.remove(i);
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else {
+ return false; // not found
+ }
+ }
+
+ // several ClientRanges: remove one, potentially splitting into many IntRanges.
+ // Save the original start and end id for the original IntRange
+ // in case the radio update fails and we have to revert it. If the
+ // update succeeds, we remove the client range and insert the new IntRanges.
+ int largestEndId = Integer.MIN_VALUE; // largest end identifier found
+ boolean updateStarted = false;
+
+ for (int crIndex=0; crIndex < crLength; crIndex++) {
+ ClientRange cr = clients.get(crIndex);
+ if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+ // found the ClientRange to remove, check if it's the last in the list
+ if (crIndex == crLength - 1) {
+ if (range.endId == largestEndId) {
+ // no channels to remove from radio; return success
+ clients.remove(crIndex);
+ return true;
+ } else {
+ // disable the channels at the end and lower the end id
+ if (tryAddSingleRange(largestEndId + 1, range.endId, false)) {
+ clients.remove(crIndex);
+ range.endId = largestEndId;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ // copy the IntRange so that we can remove elements and modify the
+ // start and end id's in the copy, leaving the original unmodified
+ // until after the radio update succeeds
+ IntRange rangeCopy = new IntRange(range, crIndex);
+
+ if (crIndex == 0) {
+ // removing the first ClientRange, so we may need to increase
+ // the start id of the IntRange.
+ // We know there are at least two ClientRanges in the list,
+ // so clients.get(1) should always succeed.
+ int nextStartId = clients.get(1).startId;
+ if (nextStartId != range.startId) {
+ startUpdate();
+ updateStarted = true;
+ addRange(range.startId, nextStartId - 1, false);
+ rangeCopy.startId = nextStartId;
+ }
+ // init largestEndId
+ largestEndId = clients.get(1).endId;
+ }
+
+ // go through remaining ClientRanges, creating new IntRanges when
+ // there is a gap in the sequence. After radio update succeeds,
+ // remove the original IntRange and append newRanges to mRanges.
+ // Otherwise, leave the original IntRange in mRanges and return false.
+ ArrayList newRanges = new ArrayList();
+
+ IntRange currentRange = rangeCopy;
+ for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
+ ClientRange nextCr = clients.get(nextIndex);
+ if (nextCr.startId > largestEndId + 1) {
+ if (!updateStarted) {
+ startUpdate();
+ updateStarted = true;
+ }
+ addRange(largestEndId + 1, nextCr.startId - 1, false);
+ currentRange.endId = largestEndId;
+ newRanges.add(currentRange);
+ currentRange = new IntRange(nextCr);
+ } else {
+ currentRange.clients.add(nextCr);
+ }
+ if (nextCr.endId > largestEndId) {
+ largestEndId = nextCr.endId;
+ }
+ }
+
+ // remove any channels between largestEndId and endId
+ if (largestEndId < endId) {
+ if (!updateStarted) {
+ startUpdate();
+ updateStarted = true;
+ }
+ addRange(largestEndId + 1, endId, false);
+ currentRange.endId = largestEndId;
+ }
+ newRanges.add(currentRange);
+
+ if (updateStarted && !finishUpdate()) {
+ return false; // failed to update radio
+ }
+
+ // replace the original IntRange with newRanges
+ mRanges.remove(i);
+ mRanges.addAll(i, newRanges);
+ return true;
+ } else {
+ // not the ClientRange to remove; save highest end ID seen so far
+ if (cr.endId > largestEndId) {
+ largestEndId = cr.endId;
+ }
+ }
+ }
+ }
+ }
+
+ return false; // not found
+ }
+
+ /**
+ * Perform a complete update operation (enable all ranges). Useful
+ * after a radio reset. Calls {@link #startUpdate}, followed by zero or
+ * more calls to {@link #addRange}, followed by {@link #finishUpdate}.
+ * @return true if successful, false otherwise
+ */
+ public boolean updateRanges() {
+ startUpdate();
+ Iterator iterator = mRanges.iterator();
+ if (iterator.hasNext()) {
+ IntRange range = iterator.next();
+ int start = range.startId;
+ int end = range.endId;
+ // accumulate ranges of [startId, endId]
+ while (iterator.hasNext()) {
+ IntRange nextNode = iterator.next();
+ // [startIdA, endIdA], [endIdA + 1, endIdB] -> [startIdA, endIdB]
+ if (nextNode.startId <= (end + 1)) {
+ if (nextNode.endId > end) {
+ end = nextNode.endId;
+ }
+ } else {
+ addRange(start, end, true);
+ start = nextNode.startId;
+ end = nextNode.endId;
+ }
+ }
+ // add final range
+ addRange(start, end, true);
+ }
+ return finishUpdate();
+ }
+
+ /**
+ * Enable or disable a single range of message identifiers.
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param selected true to enable range, false to disable range
+ * @return true if successful, false otherwise
+ */
+ private boolean tryAddSingleRange(int startId, int endId, boolean selected) {
+ startUpdate();
+ addRange(startId, endId, selected);
+ return finishUpdate();
+ }
+
+ /**
+ * Returns whether the list of ranges is completely empty.
+ * @return true if there are no enabled ranges
+ */
+ public boolean isEmpty() {
+ return mRanges.isEmpty();
+ }
+
+ /**
+ * Called when the list of enabled ranges has changed. This will be
+ * followed by zero or more calls to {@link #addRange} followed by
+ * a call to {@link #finishUpdate}.
+ */
+ protected abstract void startUpdate();
+
+ /**
+ * Called after {@link #startUpdate} to indicate a range of enabled
+ * or disabled values.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param selected true to enable range, false to disable range
+ */
+ protected abstract void addRange(int startId, int endId, boolean selected);
+
+ /**
+ * Called to indicate the end of a range update started by the
+ * previous call to {@link #startUpdate}.
+ * @return true if successful, false otherwise
+ */
+ protected abstract boolean finishUpdate();
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb33521265afa0ac25d8e084d57647f7ad58bfc4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -0,0 +1,572 @@
+/*
+ * 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.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Locale;
+import libcore.icu.TimeZones;
+
+/**
+ * Mobile Country Code
+ *
+ * {@hide}
+ */
+public final class MccTable
+{
+ static final String LOG_TAG = "MccTable";
+
+ static ArrayList table;
+
+ static class MccEntry implements Comparable
+ {
+ int mcc;
+ String iso;
+ int smallestDigitsMnc;
+ String language;
+
+ MccEntry(int mnc, String iso, int smallestDigitsMCC) {
+ this(mnc, iso, smallestDigitsMCC, null);
+ }
+
+ MccEntry(int mnc, String iso, int smallestDigitsMCC, String language) {
+ this.mcc = mnc;
+ this.iso = iso;
+ this.smallestDigitsMnc = smallestDigitsMCC;
+ this.language = language;
+ }
+
+
+ public int compareTo(MccEntry o)
+ {
+ return mcc - o.mcc;
+ }
+ }
+
+ private static MccEntry
+ entryForMcc(int mcc)
+ {
+ int index;
+
+ MccEntry m;
+
+ m = new MccEntry(mcc, null, 0);
+
+ index = Collections.binarySearch(table, m);
+
+ if (index < 0) {
+ return null;
+ } else {
+ return table.get(index);
+ }
+ }
+
+ /**
+ * Returns a default time zone ID for the given MCC.
+ * @param mcc Mobile Country Code
+ * @return default TimeZone ID, or null if not specified
+ */
+ public static String defaultTimeZoneForMcc(int mcc) {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+ if (entry == null || entry.iso == null) {
+ return null;
+ } else {
+ Locale locale;
+ if (entry.language == null) {
+ locale = new Locale(entry.iso);
+ } else {
+ locale = new Locale(entry.language, entry.iso);
+ }
+ String[] tz = TimeZones.forLocale(locale);
+ if (tz.length == 0) return null;
+ return tz[0];
+ }
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns
+ * an ISO two-character country code if available.
+ * Returns "" if unavailable.
+ */
+ public static String
+ countryCodeForMcc(int mcc)
+ {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
+ return "";
+ } else {
+ return entry.iso;
+ }
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns
+ * an ISO 2-3 character language code if available.
+ * Returns null if unavailable.
+ */
+ public static String defaultLanguageForMcc(int mcc) {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
+ return null;
+ } else {
+ return entry.language;
+ }
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns
+ * the smallest number of digits that M if available.
+ * Returns 2 if unavailable.
+ */
+ public static int
+ smallestDigitsMccForMnc(int mcc)
+ {
+ MccEntry entry;
+
+ entry = entryForMcc(mcc);
+
+ if (entry == null) {
+ return 2;
+ } else {
+ return entry.smallestDigitsMnc;
+ }
+ }
+
+ /**
+ * Updates MCC and MNC device configuration information for application retrieving
+ * correct version of resources. If either MCC or MNC is 0, they will be ignored (not set).
+ * @param context Context to act on.
+ * @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end
+ */
+ public static void updateMccMncConfiguration(Context context, String mccmnc) {
+ if (!TextUtils.isEmpty(mccmnc)) {
+ int mcc, mnc;
+
+ try {
+ mcc = Integer.parseInt(mccmnc.substring(0,3));
+ mnc = Integer.parseInt(mccmnc.substring(3));
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Error parsing IMSI");
+ return;
+ }
+
+ Log.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);
+
+ if (mcc != 0) {
+ setTimezoneFromMccIfNeeded(context, mcc);
+ setLocaleFromMccIfNeeded(context, mcc);
+ setWifiCountryCodeFromMcc(context, mcc);
+ }
+ try {
+ Configuration config = ActivityManagerNative.getDefault().getConfiguration();
+ if (mcc != 0) {
+ config.mcc = mcc;
+ }
+ if (mnc != 0) {
+ config.mnc = mnc;
+ }
+ ActivityManagerNative.getDefault().updateConfiguration(config);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't update configuration", e);
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ String l = SystemProperties.get("persist.sys.language");
+ String c = SystemProperties.get("persist.sys.country");
+
+ if (null == language) {
+ return; // no match possible
+ }
+ language = language.toLowerCase();
+ if (null == country) {
+ country = "";
+ }
+ country = country.toUpperCase();
+
+ if((null == l || 0 == l.length()) && (null == c || 0 == c.length())) {
+ try {
+ // try to find a good match
+ String[] locales = context.getAssets().getLocales();
+ final int N = locales.length;
+ String bestMatch = null;
+ for(int i = 0; i < N; i++) {
+ // only match full (lang + country) locales
+ if (locales[i]!=null && locales[i].length() >= 5 &&
+ locales[i].substring(0,2).equals(language)) {
+ if (locales[i].substring(3,5).equals(country)) {
+ bestMatch = locales[i];
+ break;
+ } else if (null == bestMatch) {
+ bestMatch = locales[i];
+ }
+ }
+ }
+ if (null != bestMatch) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ Configuration config = am.getConfiguration();
+ config.locale = new Locale(bestMatch.substring(0,2),
+ bestMatch.substring(3,5));
+ config.userSetLocale = true;
+ am.updateConfiguration(config);
+ }
+ } catch (Exception e) {
+ // Intentionally left blank
+ }
+ }
+ }
+
+ /**
+ * If the timezone is not already set, set it 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)
+ */
+ private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
+ String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
+ if (timezone == null || timezone.length() == 0) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ // Set time zone based on MCC
+ AlarmManager alarm =
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarm.setTimeZone(zoneId);
+ Log.d(LOG_TAG, "timezone set to "+zoneId);
+ }
+ }
+ }
+
+ /**
+ * If the locale is not already set, set it 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)
+ */
+ private static void setLocaleFromMccIfNeeded(Context context, int mcc) {
+ if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ // Avoid system locale is set from MCC table if CDMALTEPhone is used.
+ // The locale will be picked up based on EFpl/EFli once CSIM records are loaded.
+ return;
+ }
+ String language = MccTable.defaultLanguageForMcc(mcc);
+ String country = MccTable.countryCodeForMcc(mcc);
+
+ Log.d(LOG_TAG, "locale set to "+language+"_"+country);
+ setSystemLocale(context, language, country);
+ }
+
+ /**
+ * If the number of allowed wifi channels has not been set, set it 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)
+ */
+ private static void setWifiCountryCodeFromMcc(Context context, int mcc) {
+ String country = MccTable.countryCodeForMcc(mcc);
+ if (!country.isEmpty()) {
+ Log.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country);
+ WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ //persist
+ wM.setCountryCode(country, true);
+ }
+ }
+
+ static {
+ table = new ArrayList(240);
+
+
+ /*
+ * The table below is built from two resources:
+ *
+ * 1) ITU "Mobile Network Code (MNC) for the international
+ * identification plan for mobile terminals and mobile users"
+ * which is available as an annex to the ITU operational bulletin
+ * available here: http://www.itu.int/itu-t/bulletin/annex.html
+ *
+ * 2) The ISO 3166 country codes list, available here:
+ * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
+ *
+ * This table has not been verified.
+ *
+ */
+
+ table.add(new MccEntry(202,"gr",2)); //Greece
+ table.add(new MccEntry(204,"nl",2,"nl")); //Netherlands (Kingdom of the)
+ table.add(new MccEntry(206,"be",2)); //Belgium
+ table.add(new MccEntry(208,"fr",2,"fr")); //France
+ table.add(new MccEntry(212,"mc",2)); //Monaco (Principality of)
+ table.add(new MccEntry(213,"ad",2)); //Andorra (Principality of)
+ table.add(new MccEntry(214,"es",2,"es")); //Spain
+ table.add(new MccEntry(216,"hu",2)); //Hungary (Republic of)
+ table.add(new MccEntry(218,"ba",2)); //Bosnia and Herzegovina
+ table.add(new MccEntry(219,"hr",2)); //Croatia (Republic of)
+ table.add(new MccEntry(220,"rs",2)); //Serbia and Montenegro
+ table.add(new MccEntry(222,"it",2,"it")); //Italy
+ table.add(new MccEntry(225,"va",2,"it")); //Vatican City State
+ table.add(new MccEntry(226,"ro",2)); //Romania
+ table.add(new MccEntry(228,"ch",2,"de")); //Switzerland (Confederation of)
+ table.add(new MccEntry(230,"cz",2,"cs")); //Czech Republic
+ table.add(new MccEntry(231,"sk",2)); //Slovak Republic
+ table.add(new MccEntry(232,"at",2,"de")); //Austria
+ table.add(new MccEntry(234,"gb",2,"en")); //United Kingdom of Great Britain and Northern Ireland
+ table.add(new MccEntry(235,"gb",2,"en")); //United Kingdom of Great Britain and Northern Ireland
+ table.add(new MccEntry(238,"dk",2)); //Denmark
+ table.add(new MccEntry(240,"se",2)); //Sweden
+ table.add(new MccEntry(242,"no",2)); //Norway
+ table.add(new MccEntry(244,"fi",2)); //Finland
+ table.add(new MccEntry(246,"lt",2)); //Lithuania (Republic of)
+ table.add(new MccEntry(247,"lv",2)); //Latvia (Republic of)
+ table.add(new MccEntry(248,"ee",2)); //Estonia (Republic of)
+ table.add(new MccEntry(250,"ru",2)); //Russian Federation
+ table.add(new MccEntry(255,"ua",2)); //Ukraine
+ table.add(new MccEntry(257,"by",2)); //Belarus (Republic of)
+ table.add(new MccEntry(259,"md",2)); //Moldova (Republic of)
+ table.add(new MccEntry(260,"pl",2)); //Poland (Republic of)
+ table.add(new MccEntry(262,"de",2,"de")); //Germany (Federal Republic of)
+ table.add(new MccEntry(266,"gi",2)); //Gibraltar
+ table.add(new MccEntry(268,"pt",2)); //Portugal
+ table.add(new MccEntry(270,"lu",2)); //Luxembourg
+ table.add(new MccEntry(272,"ie",2,"en")); //Ireland
+ table.add(new MccEntry(274,"is",2)); //Iceland
+ table.add(new MccEntry(276,"al",2)); //Albania (Republic of)
+ table.add(new MccEntry(278,"mt",2)); //Malta
+ table.add(new MccEntry(280,"cy",2)); //Cyprus (Republic of)
+ table.add(new MccEntry(282,"ge",2)); //Georgia
+ table.add(new MccEntry(283,"am",2)); //Armenia (Republic of)
+ table.add(new MccEntry(284,"bg",2)); //Bulgaria (Republic of)
+ table.add(new MccEntry(286,"tr",2)); //Turkey
+ table.add(new MccEntry(288,"fo",2)); //Faroe Islands
+ table.add(new MccEntry(289,"ge",2)); //Abkhazia (Georgia)
+ table.add(new MccEntry(290,"gl",2)); //Greenland (Denmark)
+ table.add(new MccEntry(292,"sm",2)); //San Marino (Republic of)
+ table.add(new MccEntry(293,"si",2)); //Slovenia (Republic of)
+ table.add(new MccEntry(294,"mk",2)); //The Former Yugoslav Republic of Macedonia
+ table.add(new MccEntry(295,"li",2)); //Liechtenstein (Principality of)
+ table.add(new MccEntry(297,"me",2)); //Montenegro (Republic of)
+ table.add(new MccEntry(302,"ca",3,"")); //Canada
+ table.add(new MccEntry(308,"pm",2)); //Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)
+ table.add(new MccEntry(310,"us",3,"en")); //United States of America
+ table.add(new MccEntry(311,"us",3,"en")); //United States of America
+ table.add(new MccEntry(312,"us",3,"en")); //United States of America
+ table.add(new MccEntry(313,"us",3,"en")); //United States of America
+ table.add(new MccEntry(314,"us",3,"en")); //United States of America
+ table.add(new MccEntry(315,"us",3,"en")); //United States of America
+ table.add(new MccEntry(316,"us",3,"en")); //United States of America
+ table.add(new MccEntry(330,"pr",2)); //Puerto Rico
+ table.add(new MccEntry(332,"vi",2)); //United States Virgin Islands
+ table.add(new MccEntry(334,"mx",3)); //Mexico
+ table.add(new MccEntry(338,"jm",3)); //Jamaica
+ table.add(new MccEntry(340,"gp",2)); //Guadeloupe (French Department of)
+ table.add(new MccEntry(342,"bb",3)); //Barbados
+ table.add(new MccEntry(344,"ag",3)); //Antigua and Barbuda
+ table.add(new MccEntry(346,"ky",3)); //Cayman Islands
+ table.add(new MccEntry(348,"vg",3)); //British Virgin Islands
+ table.add(new MccEntry(350,"bm",2)); //Bermuda
+ table.add(new MccEntry(352,"gd",2)); //Grenada
+ table.add(new MccEntry(354,"ms",2)); //Montserrat
+ table.add(new MccEntry(356,"kn",2)); //Saint Kitts and Nevis
+ table.add(new MccEntry(358,"lc",2)); //Saint Lucia
+ table.add(new MccEntry(360,"vc",2)); //Saint Vincent and the Grenadines
+ table.add(new MccEntry(362,"ai",2)); //Netherlands Antilles
+ table.add(new MccEntry(363,"aw",2)); //Aruba
+ table.add(new MccEntry(364,"bs",2)); //Bahamas (Commonwealth of the)
+ table.add(new MccEntry(365,"ai",3)); //Anguilla
+ table.add(new MccEntry(366,"dm",2)); //Dominica (Commonwealth of)
+ table.add(new MccEntry(368,"cu",2)); //Cuba
+ table.add(new MccEntry(370,"do",2)); //Dominican Republic
+ table.add(new MccEntry(372,"ht",2)); //Haiti (Republic of)
+ table.add(new MccEntry(374,"tt",2)); //Trinidad and Tobago
+ table.add(new MccEntry(376,"tc",2)); //Turks and Caicos Islands
+ table.add(new MccEntry(400,"az",2)); //Azerbaijani Republic
+ table.add(new MccEntry(401,"kz",2)); //Kazakhstan (Republic of)
+ table.add(new MccEntry(402,"bt",2)); //Bhutan (Kingdom of)
+ table.add(new MccEntry(404,"in",2)); //India (Republic of)
+ table.add(new MccEntry(405,"in",2)); //India (Republic of)
+ table.add(new MccEntry(410,"pk",2)); //Pakistan (Islamic Republic of)
+ table.add(new MccEntry(412,"af",2)); //Afghanistan
+ table.add(new MccEntry(413,"lk",2)); //Sri Lanka (Democratic Socialist Republic of)
+ table.add(new MccEntry(414,"mm",2)); //Myanmar (Union of)
+ table.add(new MccEntry(415,"lb",2)); //Lebanon
+ table.add(new MccEntry(416,"jo",2)); //Jordan (Hashemite Kingdom of)
+ table.add(new MccEntry(417,"sy",2)); //Syrian Arab Republic
+ table.add(new MccEntry(418,"iq",2)); //Iraq (Republic of)
+ table.add(new MccEntry(419,"kw",2)); //Kuwait (State of)
+ table.add(new MccEntry(420,"sa",2)); //Saudi Arabia (Kingdom of)
+ table.add(new MccEntry(421,"ye",2)); //Yemen (Republic of)
+ table.add(new MccEntry(422,"om",2)); //Oman (Sultanate of)
+ table.add(new MccEntry(423,"ps",2)); //Palestine
+ table.add(new MccEntry(424,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(425,"il",2)); //Israel (State of)
+ table.add(new MccEntry(426,"bh",2)); //Bahrain (Kingdom of)
+ table.add(new MccEntry(427,"qa",2)); //Qatar (State of)
+ table.add(new MccEntry(428,"mn",2)); //Mongolia
+ table.add(new MccEntry(429,"np",2)); //Nepal
+ table.add(new MccEntry(430,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(431,"ae",2)); //United Arab Emirates
+ table.add(new MccEntry(432,"ir",2)); //Iran (Islamic Republic of)
+ table.add(new MccEntry(434,"uz",2)); //Uzbekistan (Republic of)
+ table.add(new MccEntry(436,"tj",2)); //Tajikistan (Republic of)
+ table.add(new MccEntry(437,"kg",2)); //Kyrgyz Republic
+ table.add(new MccEntry(438,"tm",2)); //Turkmenistan
+ table.add(new MccEntry(440,"jp",2,"ja")); //Japan
+ table.add(new MccEntry(441,"jp",2,"ja")); //Japan
+ table.add(new MccEntry(450,"kr",2,"ko")); //Korea (Republic of)
+ table.add(new MccEntry(452,"vn",2)); //Viet Nam (Socialist Republic of)
+ table.add(new MccEntry(454,"hk",2)); //"Hong Kong, China"
+ table.add(new MccEntry(455,"mo",2)); //"Macao, China"
+ table.add(new MccEntry(456,"kh",2)); //Cambodia (Kingdom of)
+ table.add(new MccEntry(457,"la",2)); //Lao People's Democratic Republic
+ table.add(new MccEntry(460,"cn",2,"zh")); //China (People's Republic of)
+ table.add(new MccEntry(461,"cn",2,"zh")); //China (People's Republic of)
+ table.add(new MccEntry(466,"tw",2)); //"Taiwan, China"
+ table.add(new MccEntry(467,"kp",2)); //Democratic People's Republic of Korea
+ table.add(new MccEntry(470,"bd",2)); //Bangladesh (People's Republic of)
+ table.add(new MccEntry(472,"mv",2)); //Maldives (Republic of)
+ table.add(new MccEntry(502,"my",2)); //Malaysia
+ table.add(new MccEntry(505,"au",2,"en")); //Australia
+ table.add(new MccEntry(510,"id",2)); //Indonesia (Republic of)
+ table.add(new MccEntry(514,"tl",2)); //Democratic Republic of Timor-Leste
+ table.add(new MccEntry(515,"ph",2)); //Philippines (Republic of the)
+ table.add(new MccEntry(520,"th",2)); //Thailand
+ table.add(new MccEntry(525,"sg",2,"en")); //Singapore (Republic of)
+ table.add(new MccEntry(528,"bn",2)); //Brunei Darussalam
+ table.add(new MccEntry(530,"nz",2, "en")); //New Zealand
+ table.add(new MccEntry(534,"mp",2)); //Northern Mariana Islands (Commonwealth of the)
+ table.add(new MccEntry(535,"gu",2)); //Guam
+ table.add(new MccEntry(536,"nr",2)); //Nauru (Republic of)
+ table.add(new MccEntry(537,"pg",2)); //Papua New Guinea
+ table.add(new MccEntry(539,"to",2)); //Tonga (Kingdom of)
+ table.add(new MccEntry(540,"sb",2)); //Solomon Islands
+ table.add(new MccEntry(541,"vu",2)); //Vanuatu (Republic of)
+ table.add(new MccEntry(542,"fj",2)); //Fiji (Republic of)
+ table.add(new MccEntry(543,"wf",2)); //Wallis and Futuna (Territoire franais d'outre-mer)
+ table.add(new MccEntry(544,"as",2)); //American Samoa
+ table.add(new MccEntry(545,"ki",2)); //Kiribati (Republic of)
+ table.add(new MccEntry(546,"nc",2)); //New Caledonia (Territoire franais d'outre-mer)
+ table.add(new MccEntry(547,"pf",2)); //French Polynesia (Territoire franais d'outre-mer)
+ table.add(new MccEntry(548,"ck",2)); //Cook Islands
+ table.add(new MccEntry(549,"ws",2)); //Samoa (Independent State of)
+ table.add(new MccEntry(550,"fm",2)); //Micronesia (Federated States of)
+ table.add(new MccEntry(551,"mh",2)); //Marshall Islands (Republic of the)
+ table.add(new MccEntry(552,"pw",2)); //Palau (Republic of)
+ table.add(new MccEntry(602,"eg",2)); //Egypt (Arab Republic of)
+ table.add(new MccEntry(603,"dz",2)); //Algeria (People's Democratic Republic of)
+ table.add(new MccEntry(604,"ma",2)); //Morocco (Kingdom of)
+ table.add(new MccEntry(605,"tn",2)); //Tunisia
+ table.add(new MccEntry(606,"ly",2)); //Libya (Socialist People's Libyan Arab Jamahiriya)
+ table.add(new MccEntry(607,"gm",2)); //Gambia (Republic of the)
+ table.add(new MccEntry(608,"sn",2)); //Senegal (Republic of)
+ table.add(new MccEntry(609,"mr",2)); //Mauritania (Islamic Republic of)
+ table.add(new MccEntry(610,"ml",2)); //Mali (Republic of)
+ table.add(new MccEntry(611,"gn",2)); //Guinea (Republic of)
+ table.add(new MccEntry(612,"ci",2)); //Cte d'Ivoire (Republic of)
+ table.add(new MccEntry(613,"bf",2)); //Burkina Faso
+ table.add(new MccEntry(614,"ne",2)); //Niger (Republic of the)
+ table.add(new MccEntry(615,"tg",2)); //Togolese Republic
+ table.add(new MccEntry(616,"bj",2)); //Benin (Republic of)
+ table.add(new MccEntry(617,"mu",2)); //Mauritius (Republic of)
+ table.add(new MccEntry(618,"lr",2)); //Liberia (Republic of)
+ table.add(new MccEntry(619,"sl",2)); //Sierra Leone
+ table.add(new MccEntry(620,"gh",2)); //Ghana
+ table.add(new MccEntry(621,"ng",2)); //Nigeria (Federal Republic of)
+ table.add(new MccEntry(622,"td",2)); //Chad (Republic of)
+ table.add(new MccEntry(623,"cf",2)); //Central African Republic
+ table.add(new MccEntry(624,"cm",2)); //Cameroon (Republic of)
+ table.add(new MccEntry(625,"cv",2)); //Cape Verde (Republic of)
+ table.add(new MccEntry(626,"st",2)); //Sao Tome and Principe (Democratic Republic of)
+ table.add(new MccEntry(627,"gq",2)); //Equatorial Guinea (Republic of)
+ table.add(new MccEntry(628,"ga",2)); //Gabonese Republic
+ table.add(new MccEntry(629,"cg",2)); //Congo (Republic of the)
+ table.add(new MccEntry(630,"cg",2)); //Democratic Republic of the Congo
+ table.add(new MccEntry(631,"ao",2)); //Angola (Republic of)
+ table.add(new MccEntry(632,"gw",2)); //Guinea-Bissau (Republic of)
+ table.add(new MccEntry(633,"sc",2)); //Seychelles (Republic of)
+ table.add(new MccEntry(634,"sd",2)); //Sudan (Republic of the)
+ table.add(new MccEntry(635,"rw",2)); //Rwanda (Republic of)
+ table.add(new MccEntry(636,"et",2)); //Ethiopia (Federal Democratic Republic of)
+ table.add(new MccEntry(637,"so",2)); //Somali Democratic Republic
+ table.add(new MccEntry(638,"dj",2)); //Djibouti (Republic of)
+ table.add(new MccEntry(639,"ke",2)); //Kenya (Republic of)
+ table.add(new MccEntry(640,"tz",2)); //Tanzania (United Republic of)
+ table.add(new MccEntry(641,"ug",2)); //Uganda (Republic of)
+ table.add(new MccEntry(642,"bi",2)); //Burundi (Republic of)
+ table.add(new MccEntry(643,"mz",2)); //Mozambique (Republic of)
+ table.add(new MccEntry(645,"zm",2)); //Zambia (Republic of)
+ table.add(new MccEntry(646,"mg",2)); //Madagascar (Republic of)
+ table.add(new MccEntry(647,"re",2)); //Reunion (French Department of)
+ table.add(new MccEntry(648,"zw",2)); //Zimbabwe (Republic of)
+ table.add(new MccEntry(649,"na",2)); //Namibia (Republic of)
+ table.add(new MccEntry(650,"mw",2)); //Malawi
+ table.add(new MccEntry(651,"ls",2)); //Lesotho (Kingdom of)
+ table.add(new MccEntry(652,"bw",2)); //Botswana (Republic of)
+ table.add(new MccEntry(653,"sz",2)); //Swaziland (Kingdom of)
+ table.add(new MccEntry(654,"km",2)); //Comoros (Union of the)
+ table.add(new MccEntry(655,"za",2,"en")); //South Africa (Republic of)
+ table.add(new MccEntry(657,"er",2)); //Eritrea
+ table.add(new MccEntry(702,"bz",2)); //Belize
+ table.add(new MccEntry(704,"gt",2)); //Guatemala (Republic of)
+ table.add(new MccEntry(706,"sv",2)); //El Salvador (Republic of)
+ table.add(new MccEntry(708,"hn",3)); //Honduras (Republic of)
+ table.add(new MccEntry(710,"ni",2)); //Nicaragua
+ table.add(new MccEntry(712,"cr",2)); //Costa Rica
+ table.add(new MccEntry(714,"pa",2)); //Panama (Republic of)
+ table.add(new MccEntry(716,"pe",2)); //Peru
+ table.add(new MccEntry(722,"ar",3)); //Argentine Republic
+ table.add(new MccEntry(724,"br",2)); //Brazil (Federative Republic of)
+ table.add(new MccEntry(730,"cl",2)); //Chile
+ table.add(new MccEntry(732,"co",3)); //Colombia (Republic of)
+ table.add(new MccEntry(734,"ve",2)); //Venezuela (Bolivarian Republic of)
+ table.add(new MccEntry(736,"bo",2)); //Bolivia (Republic of)
+ table.add(new MccEntry(738,"gy",2)); //Guyana
+ table.add(new MccEntry(740,"ec",2)); //Ecuador
+ table.add(new MccEntry(742,"gf",2)); //French Guiana (French Department of)
+ table.add(new MccEntry(744,"py",2)); //Paraguay (Republic of)
+ table.add(new MccEntry(746,"sr",2)); //Suriname (Republic of)
+ table.add(new MccEntry(748,"uy",2)); //Uruguay (Eastern Republic of)
+ table.add(new MccEntry(750,"fk",2)); //Falkland Islands (Malvinas)
+ //table.add(new MccEntry(901,"",2)); //"International Mobile, shared code"
+
+ Collections.sort(table);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/MmiCode.java b/src/java/com/android/internal/telephony/MmiCode.java
new file mode 100644
index 0000000000000000000000000000000000000000..c71ff778d8592be196de894fd1535a5f572b7400
--- /dev/null
+++ b/src/java/com/android/internal/telephony/MmiCode.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+public interface MmiCode
+{
+ /**
+ * {@hide}
+ */
+ public enum State {
+ PENDING,
+ CANCELLED,
+ COMPLETE,
+ FAILED
+ }
+
+
+ /**
+ * @return Current state of MmiCode request
+ */
+ public State getState();
+
+ /**
+ * @return Localized message for UI display, valid only in COMPLETE
+ * or FAILED states. null otherwise
+ */
+
+ public CharSequence getMessage();
+
+ /**
+ * Cancels pending MMI request.
+ * State becomes CANCELLED unless already COMPLETE or FAILED
+ */
+ public void cancel();
+
+ /**
+ * @return true if the network response is a REQUEST for more user input.
+ */
+ public boolean isUssdRequest();
+
+ /**
+ * @return true if an outstanding request can be canceled.
+ */
+ public boolean isCancelable();
+}
diff --git a/src/java/com/android/internal/telephony/OperatorInfo.java b/src/java/com/android/internal/telephony/OperatorInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1999cb3ada4f74088302c37d0f3c603b36ded245
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OperatorInfo.java
@@ -0,0 +1,149 @@
+/*
+ * 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 operatorAlphaLong;
+ private String operatorAlphaShort;
+ private String operatorNumeric;
+
+ private State state = State.UNKNOWN;
+
+
+ public String
+ getOperatorAlphaLong() {
+ return operatorAlphaLong;
+ }
+
+ public String
+ getOperatorAlphaShort() {
+ return operatorAlphaShort;
+ }
+
+ public String
+ getOperatorNumeric() {
+ return operatorNumeric;
+ }
+
+ public State
+ getState() {
+ return state;
+ }
+
+ OperatorInfo(String operatorAlphaLong,
+ String operatorAlphaShort,
+ String operatorNumeric,
+ State state) {
+
+ this.operatorAlphaLong = operatorAlphaLong;
+ this.operatorAlphaShort = operatorAlphaShort;
+ this.operatorNumeric = operatorNumeric;
+
+ this.state = 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 + "'");
+ }
+ }
+
+
+ public String toString() {
+ return "OperatorInfo " + operatorAlphaLong
+ + "/" + operatorAlphaShort
+ + "/" + operatorNumeric
+ + "/" + state;
+ }
+
+ /**
+ * 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.
+ */
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * Method to serialize a OperatorInfo object.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(operatorAlphaLong);
+ dest.writeString(operatorAlphaShort);
+ dest.writeString(operatorNumeric);
+ dest.writeSerializable(state);
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * Method to deserialize a OperatorInfo object, or an array thereof.
+ */
+ public static final Creator CREATOR =
+ new Creator() {
+ public OperatorInfo createFromParcel(Parcel in) {
+ OperatorInfo opInfo = new OperatorInfo(
+ in.readString(), /*operatorAlphaLong*/
+ in.readString(), /*operatorAlphaShort*/
+ in.readString(), /*operatorNumeric*/
+ (State) in.readSerializable()); /*state*/
+ return opInfo;
+ }
+
+ 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
new file mode 100644
index 0000000000000000000000000000000000000000..34aa96c7fd8a79cd0966c1d0a292fbb2b09254be
--- /dev/null
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -0,0 +1,1695 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.Context;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+
+import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.gsm.UsimServiceTable;
+import com.android.internal.telephony.ims.IsimRecords;
+import com.android.internal.telephony.test.SimulatedRadioControl;
+
+import com.android.internal.telephony.PhoneConstants.*; // ????
+
+import java.util.List;
+
+/**
+ * Internal interface used to control the phone; SDK developers cannot
+ * obtain this interface.
+ *
+ * {@hide}
+ *
+ */
+public interface Phone {
+
+ /** used to enable additional debug messages */
+ static final boolean DEBUG_PHONE = true;
+
+ public enum DataActivityState {
+ /**
+ * The state of a data activity.
+ *
+ * - NONE = No traffic
+ * - DATAIN = Receiving IP ppp traffic
+ * - DATAOUT = Sending IP ppp traffic
+ * - DATAINANDOUT = Both receiving and sending IP ppp traffic
+ * - DORMANT = The data connection is still active,
+ but physical link is down
+ *
+ */
+ NONE, DATAIN, DATAOUT, DATAINANDOUT, DORMANT;
+ };
+
+ enum SuppService {
+ UNKNOWN, SWITCH, SEPARATE, TRANSFER, CONFERENCE, REJECT, HANGUP;
+ };
+
+ // "Features" accessible through the connectivity manager
+ static final String FEATURE_ENABLE_MMS = "enableMMS";
+ static final String FEATURE_ENABLE_SUPL = "enableSUPL";
+ static final String FEATURE_ENABLE_DUN = "enableDUN";
+ static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
+ static final String FEATURE_ENABLE_DUN_ALWAYS = "enableDUNAlways";
+ static final String FEATURE_ENABLE_FOTA = "enableFOTA";
+ static final String FEATURE_ENABLE_IMS = "enableIMS";
+ static final String FEATURE_ENABLE_CBS = "enableCBS";
+
+ /**
+ * Optional reasons for disconnect and connect
+ */
+ static final String REASON_ROAMING_ON = "roamingOn";
+ static final String REASON_ROAMING_OFF = "roamingOff";
+ static final String REASON_DATA_DISABLED = "dataDisabled";
+ static final String REASON_DATA_ENABLED = "dataEnabled";
+ static final String REASON_DATA_ATTACHED = "dataAttached";
+ static final String REASON_DATA_DETACHED = "dataDetached";
+ static final String REASON_CDMA_DATA_ATTACHED = "cdmaDataAttached";
+ static final String REASON_CDMA_DATA_DETACHED = "cdmaDataDetached";
+ static final String REASON_APN_CHANGED = "apnChanged";
+ static final String REASON_APN_SWITCHED = "apnSwitched";
+ static final String REASON_APN_FAILED = "apnFailed";
+ static final String REASON_RESTORE_DEFAULT_APN = "restoreDefaultApn";
+ static final String REASON_RADIO_TURNED_OFF = "radioTurnedOff";
+ static final String REASON_PDP_RESET = "pdpReset";
+ static final String REASON_VOICE_CALL_ENDED = "2GVoiceCallEnded";
+ static final String REASON_VOICE_CALL_STARTED = "2GVoiceCallStarted";
+ static final String REASON_PS_RESTRICT_ENABLED = "psRestrictEnabled";
+ static final String REASON_PS_RESTRICT_DISABLED = "psRestrictDisabled";
+ static final String REASON_SIM_LOADED = "simLoaded";
+ static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
+ static final String REASON_DATA_DEPENDENCY_MET = "dependencyMet";
+ static final String REASON_DATA_DEPENDENCY_UNMET = "dependencyUnmet";
+
+ // Used for band mode selection methods
+ static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
+ static final int BM_EURO_BAND = 1; // GSM-900 / DCS-1800 / WCDMA-IMT-2000
+ static final int BM_US_BAND = 2; // GSM-850 / PCS-1900 / WCDMA-850 / WCDMA-PCS-1900
+ static final int BM_JPN_BAND = 3; // WCDMA-800 / WCDMA-IMT-2000
+ static final int BM_AUS_BAND = 4; // GSM-900 / DCS-1800 / WCDMA-850 / WCDMA-IMT-2000
+ static final int BM_AUS2_BAND = 5; // GSM-900 / DCS-1800 / WCDMA-850
+ static final int BM_BOUNDARY = 6; // upper band boundary
+
+ // Used for preferred network type
+ // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
+ int NT_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ int NT_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+ int NT_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ int NT_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+ int NT_MODE_CDMA = RILConstants.NETWORK_MODE_CDMA;
+
+ int NT_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ int NT_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ int NT_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+ int NT_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
+ int PREFERRED_NT_MODE = RILConstants.PREFERRED_NETWORK_MODE;
+
+
+ // Used for CDMA roaming mode
+ static final int CDMA_RM_HOME = 0; // Home Networks only, as defined in PRL
+ static final int CDMA_RM_AFFILIATED = 1; // Roaming an Affiliated networks, as defined in PRL
+ static final int CDMA_RM_ANY = 2; // Roaming on Any Network, as defined in PRL
+
+ // Used for CDMA subscription mode
+ static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // RUIM/SIM (default)
+ static final int CDMA_SUBSCRIPTION_NV = 1; // NV -> non-volatile memory
+
+ static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_NV;
+
+ static final int TTY_MODE_OFF = 0;
+ static final int TTY_MODE_FULL = 1;
+ static final int TTY_MODE_HCO = 2;
+ static final int TTY_MODE_VCO = 3;
+
+ /**
+ * CDMA OTA PROVISION STATUS, the same as RIL_CDMA_OTA_Status in ril.h
+ */
+
+ public static final int CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED = 0;
+ public static final int CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED = 1;
+ public static final int CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED = 2;
+ public static final int CDMA_OTA_PROVISION_STATUS_SSD_UPDATED = 3;
+ public static final int CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED = 4;
+ public static final int CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED = 5;
+ public static final int CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED = 6;
+ public static final int CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED = 7;
+ public static final int CDMA_OTA_PROVISION_STATUS_COMMITTED = 8;
+ public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED = 9;
+ public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED = 10;
+ public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED = 11;
+
+
+ /**
+ * Get the current ServiceState. Use
+ * registerForServiceStateChanged
to be informed of
+ * updates.
+ */
+ ServiceState getServiceState();
+
+ /**
+ * Get the current CellLocation.
+ */
+ CellLocation getCellLocation();
+
+ /**
+ * Get the current for the default apn DataState. No change notification
+ * exists at this interface -- use
+ * {@link android.telephony.PhoneStateListener} instead.
+ */
+ DataState getDataConnectionState();
+
+ /**
+ * Get the current DataState. No change notification exists at this
+ * interface -- use
+ * {@link android.telephony.PhoneStateListener} instead.
+ * @param apnType specify for which apn to get connection state info.
+ */
+ DataState getDataConnectionState(String apnType);
+
+ /**
+ * Get the current DataActivityState. No change notification exists at this
+ * interface -- use
+ * {@link android.telephony.TelephonyManager} instead.
+ */
+ DataActivityState getDataActivityState();
+
+ /**
+ * Gets the context for the phone, as set at initialization time.
+ */
+ Context getContext();
+
+ /**
+ * Disables the DNS check (i.e., allows "0.0.0.0").
+ * Useful for lab testing environment.
+ * @param b true disables the check, false enables.
+ */
+ void disableDnsCheck(boolean b);
+
+ /**
+ * Returns true if the DNS check is currently disabled.
+ */
+ boolean isDnsCheckDisabled();
+
+ /**
+ * Get current coarse-grained voice call state.
+ * Use {@link #registerForPreciseCallStateChanged(Handler, int, Object)
+ * registerForPreciseCallStateChanged()} for change notification.
+ * If the phone has an active call and call waiting occurs,
+ * then the phone state is RINGING not OFFHOOK
+ * Note:
+ * This registration point provides notification of finer-grained
+ * changes.
+ *
+ */
+ State getState();
+
+ /**
+ * Returns a string identifier for this phone interface for parties
+ * outside the phone app process.
+ * @return The string name.
+ */
+ String getPhoneName();
+
+ /**
+ * Return a numerical identifier for the phone radio interface.
+ * @return PHONE_TYPE_XXX as defined above.
+ */
+ int getPhoneType();
+
+ /**
+ * Returns an array of string identifiers for the APN types serviced by the
+ * currently active.
+ * @return The string array will always return at least one entry, Phone.APN_TYPE_DEFAULT.
+ * TODO: Revisit if we always should return at least one entry.
+ */
+ String[] getActiveApnTypes();
+
+ /**
+ * Returns string for the active APN host.
+ * @return type as a string or null if none.
+ */
+ String getActiveApnHost(String apnType);
+
+ /**
+ * Return the LinkProperties for the named apn or null if not available
+ */
+ LinkProperties getLinkProperties(String apnType);
+
+ /**
+ * Return the LinkCapabilities
+ */
+ LinkCapabilities getLinkCapabilities(String apnType);
+
+ /**
+ * Get current signal strength. No change notification available on this
+ * interface. Use PhoneStateNotifier
or an equivalent.
+ * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
+ * The following special values are defined:
+ * - 0 means "-113 dBm or less".
+ * - 31 means "-51 dBm or greater".
+ *
+ * @return Current signal strength as SignalStrength
+ */
+ SignalStrength getSignalStrength();
+
+ /**
+ * Notifies when a previously untracked non-ringing/waiting connection has appeared.
+ * This is likely due to some other entity (eg, SIM card application) initiating a call.
+ */
+ void registerForUnknownConnection(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for unknown connection notifications.
+ */
+ void unregisterForUnknownConnection(Handler h);
+
+ /**
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
+ * Resulting events will have an AsyncResult in Message.obj
.
+ * AsyncResult.userData will be set to the obj argument here.
+ * The h parameter is held only by a weak reference.
+ */
+ void registerForPreciseCallStateChanged(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for voice call state change notifications.
+ * Extraneous calls are tolerated silently.
+ */
+ void unregisterForPreciseCallStateChanged(Handler h);
+
+
+ /**
+ * Notifies when a new ringing or waiting connection has appeared.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection.
+ * Please check Connection.isRinging() to make sure the Connection
+ * has not dropped since this message was posted.
+ * If Connection.isRinging() is true, then
+ * Connection.getCall() == Phone.getRingingCall()
+ */
+ void registerForNewRingingConnection(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for new ringing connection notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ void unregisterForNewRingingConnection(Handler h);
+
+ /**
+ * Notifies when an incoming call rings.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection.
+ */
+ void registerForIncomingRing(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for ring notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ void unregisterForIncomingRing(Handler h);
+
+ /**
+ * Notifies when out-band ringback tone is needed.
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play ringback tone
+ * and false to stop.
+ */
+ void registerForRingbackTone(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for ringback tone notification.
+ */
+
+ void unregisterForRingbackTone(Handler h);
+
+ /**
+ * Registers the handler to reset the uplink mute state to get
+ * uplink audio.
+ */
+ void registerForResendIncallMute(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for resend incall mute notifications.
+ */
+ void unregisterForResendIncallMute(Handler h);
+
+ /**
+ * Notifies when a voice connection has disconnected, either due to local
+ * or remote hangup or error.
+ *
+ * Messages received from this will have the following members:
+ *
- Message.obj will be an AsyncResult
+ * - AsyncResult.userObj = obj
+ * - AsyncResult.result = a Connection object that is
+ * no longer connected.
+ */
+ void registerForDisconnect(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for voice disconnection notification.
+ * Extraneous calls are tolerated silently
+ */
+ void unregisterForDisconnect(Handler h);
+
+
+ /**
+ * Register for notifications of initiation of a new MMI code request.
+ * MMI codes for GSM are discussed in 3GPP TS 22.030.
+ *
+ * Example: If Phone.dial is called with "*#31#", then the app will
+ * be notified here.
+ *
+ * The returned Message.obj
will contain an AsyncResult.
+ *
+ * obj.result
will be an "MmiCode" object.
+ */
+ void registerForMmiInitiate(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for new MMI initiate notification.
+ * Extraneous calls are tolerated silently
+ */
+ void unregisterForMmiInitiate(Handler h);
+
+ /**
+ * Register for notifications that an MMI request has completed
+ * its network activity and is in its final state. This may mean a state
+ * of COMPLETE, FAILED, or CANCELLED.
+ *
+ * Message.obj
will contain an AsyncResult.
+ * obj.result
will be an "MmiCode" object
+ */
+ void registerForMmiComplete(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for MMI complete notification.
+ * Extraneous calls are tolerated silently
+ */
+ void unregisterForMmiComplete(Handler h);
+
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h);
+
+ /**
+ * Returns a list of MMI codes that are pending. (They have initiated
+ * but have not yet completed).
+ * Presently there is only ever one.
+ * Use registerForMmiInitiate
+ * and registerForMmiComplete
for change notification.
+ */
+ public List extends MmiCode> getPendingMmiCodes();
+
+ /**
+ * Sends user response to a USSD REQUEST message. An MmiCode instance
+ * representing this response is sent to handlers registered with
+ * registerForMmiInitiate.
+ *
+ * @param ussdMessge Message to send in the response.
+ */
+ public void sendUssdResponse(String ussdMessge);
+
+ /**
+ * Register for ServiceState changed.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a ServiceState instance
+ */
+ void registerForServiceStateChanged(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for ServiceStateChange notification.
+ * Extraneous calls are tolerated silently
+ */
+ void unregisterForServiceStateChanged(Handler h);
+
+ /**
+ * Register for Supplementary Service notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForSuppServiceNotification(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for Supplementary Service notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForSuppServiceNotification(Handler h);
+
+ /**
+ * Register for notifications when a supplementary service attempt fails.
+ * Message.obj will contain an AsyncResult.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForSuppServiceFailed(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications when a supplementary service attempt fails.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForSuppServiceFailed(Handler h);
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj);
+
+ /**
+ * Unegister for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForInCallVoicePrivacyOn(Handler h);
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj);
+
+ /**
+ * Unegister for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForInCallVoicePrivacyOff(Handler h);
+
+ /**
+ * Register for notifications when CDMA OTA Provision status change
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForCdmaOtaStatusChange(Handler h, int what, Object obj);
+
+ /**
+ * Unegister for notifications when CDMA OTA Provision status change
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForCdmaOtaStatusChange(Handler h);
+
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h);
+
+ /**
+ * Returns SIM record load state. Use
+ * getSimCard().registerForReady()
for change notification.
+ *
+ * @return true if records from the SIM have been loaded and are
+ * available (if applicable). If not applicable to the underlying
+ * technology, returns true as well.
+ */
+ boolean getIccRecordsLoaded();
+
+ /**
+ * Returns the ICC card interface for this phone, or null
+ * if not applicable to underlying technology.
+ */
+ IccCard getIccCard();
+
+ /**
+ * Answers a ringing or waiting call. Active calls, if any, go on hold.
+ * Answering occurs asynchronously, and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when no call is ringing or waiting
+ */
+ void acceptCall() throws CallStateException;
+
+ /**
+ * Reject (ignore) a ringing call. In GSM, this means UDUB
+ * (User Determined User Busy). Reject occurs asynchronously,
+ * and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when no call is ringing or waiting
+ */
+ void rejectCall() throws CallStateException;
+
+ /**
+ * Places any active calls on hold, and makes any held calls
+ * active. Switch occurs asynchronously and may fail.
+ * Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if a call is ringing, waiting, or
+ * dialing/alerting. In these cases, this operation may not be performed.
+ */
+ void switchHoldingAndActive() throws CallStateException;
+
+ /**
+ * Whether or not the phone can conference in the current phone
+ * state--that is, one call holding and one call active.
+ * @return true if the phone can conference; false otherwise.
+ */
+ boolean canConference();
+
+ /**
+ * Conferences holding and active. Conference occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canConference() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ void conference() throws CallStateException;
+
+ /**
+ * Enable or disable enhanced Voice Privacy (VP). If enhanced VP is
+ * disabled, normal VP is enabled.
+ *
+ * @param enable whether true or false to enable or disable.
+ * @param onComplete a callback message when the action is completed.
+ */
+ void enableEnhancedVoicePrivacy(boolean enable, Message onComplete);
+
+ /**
+ * Get the currently set Voice Privacy (VP) mode.
+ *
+ * @param onComplete a callback message when the action is completed.
+ */
+ void getEnhancedVoicePrivacy(Message onComplete);
+
+ /**
+ * Whether or not the phone can do explicit call transfer in the current
+ * phone state--that is, one call holding and one call active.
+ * @return true if the phone can do explicit call transfer; false otherwise.
+ */
+ boolean canTransfer();
+
+ /**
+ * Connects the two calls and disconnects the subscriber from both calls
+ * Explicit Call Transfer occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canTransfer() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ void explicitCallTransfer() throws CallStateException;
+
+ /**
+ * Clears all DISCONNECTED connections from Call connection lists.
+ * Calls that were in the DISCONNECTED state become idle. This occurs
+ * synchronously.
+ */
+ void clearDisconnected();
+
+
+ /**
+ * Gets the foreground call object, which represents all connections that
+ * are dialing or active (all connections
+ * that have their audio path connected).
+ *
+ * The foreground call is a singleton object. It is constant for the life
+ * of this phone. It is never null.
+ *
+ * The foreground call will only ever be in one of these states:
+ * IDLE, ACTIVE, DIALING, ALERTING, or DISCONNECTED.
+ *
+ * State change notification is available via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ */
+ Call getForegroundCall();
+
+ /**
+ * Gets the background call object, which represents all connections that
+ * are holding (all connections that have been accepted or connected, but
+ * do not have their audio path connected).
+ *
+ * The background call is a singleton object. It is constant for the life
+ * of this phone object . It is never null.
+ *
+ * The background call will only ever be in one of these states:
+ * IDLE, HOLDING or DISCONNECTED.
+ *
+ * State change notification is available via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ */
+ Call getBackgroundCall();
+
+ /**
+ * Gets the ringing call object, which represents an incoming
+ * connection (if present) that is pending answer/accept. (This connection
+ * may be RINGING or WAITING, and there may be only one.)
+
+ * The ringing call is a singleton object. It is constant for the life
+ * of this phone. It is never null.
+ *
+ * The ringing call will only ever be in one of these states:
+ * IDLE, INCOMING, WAITING or DISCONNECTED.
+ *
+ * State change notification is available via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ */
+ Call getRingingCall();
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @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) throws CallStateException;
+
+ /**
+ * Initiate a new voice connection with supplementary User to User
+ * Information. This happens asynchronously, so you cannot assume the audio
+ * path is connected (or a call index has been assigned) until
+ * PhoneStateChanged notification has occurred.
+ *
+ * @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) throws CallStateException;
+
+ /**
+ * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
+ * without SEND (so dial
is not appropriate).
+ *
+ * @param dialString the MMI command to be executed.
+ * @return true if MMI command is executed.
+ */
+ boolean handlePinMmi(String dialString);
+
+ /**
+ * Handles in-call MMI commands. While in a call, or while receiving a
+ * call, use this to execute MMI commands.
+ * see 3GPP 20.030, section 6.5.5.1 for specs on the allowed MMI commands.
+ *
+ * @param command the MMI command to be executed.
+ * @return true if the MMI command is executed.
+ * @throws CallStateException
+ */
+ boolean handleInCallMmiCommands(String command) throws CallStateException;
+
+ /**
+ * Play a DTMF tone on the active call. Ignored if there is no active call.
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ */
+ void sendDtmf(char c);
+
+ /**
+ * Start to paly a DTMF tone on the active call. Ignored if there is no active call
+ * or there is a playing DTMF tone.
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ */
+ void startDtmf(char c);
+
+ /**
+ * Stop the playing DTMF tone. Ignored if there is no playing DTMF
+ * tone or no active call.
+ */
+ void stopDtmf();
+
+ /**
+ * send burst DTMF tone, it can send the string as single character or multiple character
+ * ignore if there is no active call or not valid digits string.
+ * Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
+ * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character,
+ * this api can send single character and multiple character, also, this api has response
+ * back to caller.
+ *
+ * @param dtmfString is string representing the dialing digit(s) in the active call
+ * @param on the DTMF ON length in milliseconds, or 0 for default
+ * @param off the DTMF OFF length in milliseconds, or 0 for default
+ * @param onComplete is the callback message when the action is processed by BP
+ *
+ */
+ void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete);
+
+ /**
+ * Sets the radio power on/off state (off is sometimes
+ * called "airplane mode"). Current state can be gotten via
+ * {@link #getServiceState()}.{@link
+ * android.telephony.ServiceState#getState() getState()}.
+ * Note: This request is asynchronous.
+ * getServiceState().getState() will not change immediately after this call.
+ * registerForServiceStateChanged() to find out when the
+ * request is complete.
+ *
+ * @param power true means "on", false means "off".
+ */
+ void setRadioPower(boolean power);
+
+ /**
+ * Get voice message waiting indicator status. No change notification
+ * available on this interface. Use PhoneStateNotifier or similar instead.
+ *
+ * @return true if there is a voice message waiting
+ */
+ boolean getMessageWaitingIndicator();
+
+ /**
+ * Get voice call forwarding indicator status. No change notification
+ * available on this interface. Use PhoneStateNotifier or similar instead.
+ *
+ * @return true if there is a voice call forwarding
+ */
+ boolean getCallForwardingIndicator();
+
+ /**
+ * Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned
+ * and {@link #getMsisdn()} will return the MSISDN on CDMA/LTE phones.
+ *
+ * @return phone number. May return null if not
+ * available or the SIM is not ready
+ */
+ String getLine1Number();
+
+ /**
+ * Returns the alpha tag associated with the msisdn number.
+ * If there is no alpha tag associated or the record is not yet available,
+ * returns a default localized string.
+ */
+ String getLine1AlphaTag();
+
+ /**
+ * Sets the MSISDN phone number in the SIM card.
+ *
+ * @param alphaTag the alpha tag associated with the MSISDN phone number
+ * (see getMsisdnAlphaTag)
+ * @param number the new MSISDN phone number to be set on the SIM.
+ * @param onComplete a callback message when the action is completed.
+ */
+ void setLine1Number(String alphaTag, String number, Message onComplete);
+
+ /**
+ * Get the voice mail access phone number. Typically dialed when the
+ * user holds the "1" key in the phone app. May return null if not
+ * available or the SIM is not ready.
+ */
+ String getVoiceMailNumber();
+
+ /**
+ * Returns unread voicemail count. This count is shown when the voicemail
+ * notification is expanded.
+ */
+ int getVoiceMessageCount();
+
+ /**
+ * Returns the alpha tag associated with the voice mail number.
+ * If there is no alpha tag associated or the record is not yet available,
+ * returns a default localized string.
+ *
+ * Please use this value instead of some other localized string when
+ * showing a name for this number in the UI. For example, call log
+ * entries should show this alpha tag.
+ *
+ * Usage of this alpha tag in the UI is a common carrier requirement.
+ */
+ String getVoiceMailAlphaTag();
+
+ /**
+ * setVoiceMailNumber
+ * sets the voicemail number in the SIM card.
+ *
+ * @param alphaTag the alpha tag associated with the voice mail number
+ * (see getVoiceMailAlphaTag)
+ * @param voiceMailNumber the new voicemail number to be set on the SIM.
+ * @param onComplete a callback message when the action is completed.
+ */
+ void setVoiceMailNumber(String alphaTag,
+ String voiceMailNumber,
+ Message onComplete);
+
+ /**
+ * getCallForwardingOptions
+ * gets a call forwarding option. The return value of
+ * ((AsyncResult)onComplete.obj) is an array of CallForwardInfo.
+ *
+ * @param commandInterfaceCFReason is one of the valid call forwarding
+ * CF_REASONS, as defined in
+ * com.android.internal.telephony.CommandsInterface.
+ * @param onComplete a callback message when the action is completed.
+ * @see com.android.internal.telephony.CallForwardInfo for details.
+ */
+ void getCallForwardingOption(int commandInterfaceCFReason,
+ Message onComplete);
+
+ /**
+ * setCallForwardingOptions
+ * sets a call forwarding option.
+ *
+ * @param commandInterfaceCFReason is one of the valid call forwarding
+ * CF_REASONS, as defined in
+ * com.android.internal.telephony.CommandsInterface.
+ * @param commandInterfaceCFAction is one of the valid call forwarding
+ * CF_ACTIONS, as defined in
+ * com.android.internal.telephony.CommandsInterface.
+ * @param dialingNumber is the target phone number to forward calls to
+ * @param timerSeconds is used by CFNRy to indicate the timeout before
+ * forwarding is attempted.
+ * @param onComplete a callback message when the action is completed.
+ */
+ void setCallForwardingOption(int commandInterfaceCFReason,
+ int commandInterfaceCFAction,
+ String dialingNumber,
+ int timerSeconds,
+ Message onComplete);
+
+ /**
+ * getOutgoingCallerIdDisplay
+ * gets outgoing caller id display. The return value of
+ * ((AsyncResult)onComplete.obj) is an array of int, with a length of 2.
+ *
+ * @param onComplete a callback message when the action is completed.
+ * @see com.android.internal.telephony.CommandsInterface#getCLIR for details.
+ */
+ void getOutgoingCallerIdDisplay(Message onComplete);
+
+ /**
+ * setOutgoingCallerIdDisplay
+ * sets a call forwarding option.
+ *
+ * @param commandInterfaceCLIRMode is one of the valid call CLIR
+ * modes, as defined in
+ * com.android.internal.telephony.CommandsInterface./code>
+ * @param onComplete a callback message when the action is completed.
+ */
+ void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
+ Message onComplete);
+
+ /**
+ * getCallWaiting
+ * gets call waiting activation state. The return value of
+ * ((AsyncResult)onComplete.obj) is an array of int, with a length of 1.
+ *
+ * @param onComplete a callback message when the action is completed.
+ * @see com.android.internal.telephony.CommandsInterface#queryCallWaiting for details.
+ */
+ void getCallWaiting(Message onComplete);
+
+ /**
+ * setCallWaiting
+ * sets a call forwarding option.
+ *
+ * @param enable is a boolean representing the state that you are
+ * requesting, true for enabled, false for disabled.
+ * @param onComplete a callback message when the action is completed.
+ */
+ void setCallWaiting(boolean enable, Message onComplete);
+
+ /**
+ * Scan available networks. This method is asynchronous; .
+ * On completion, response.obj
is set to an AsyncResult with
+ * one of the following members:.
+ *
+ * response.obj.result
will be a List
of
+ * OperatorInfo
objects, or
+ * response.obj.exception
will be set with an exception
+ * on failure.
+ *
+ */
+ void getAvailableNetworks(Message response);
+
+ /**
+ * Switches network selection mode to "automatic", re-scanning and
+ * re-selecting a network if appropriate.
+ *
+ * @param response The message to dispatch when the network selection
+ * is complete.
+ *
+ * @see #selectNetworkManually(OperatorInfo, android.os.Message )
+ */
+ void setNetworkSelectionModeAutomatic(Message response);
+
+ /**
+ * Manually selects a network. response
is
+ * dispatched when this is complete. response.obj
will be
+ * an AsyncResult, and response.obj.exception
will be non-null
+ * on failure.
+ *
+ * @see #setNetworkSelectionModeAutomatic(Message)
+ */
+ void selectNetworkManually(OperatorInfo network,
+ Message response);
+
+ /**
+ * Requests to set the preferred network type for searching and registering
+ * (CS/PS domain, RAT, and operation mode)
+ * @param networkType one of NT_*_TYPE
+ * @param response is callback message
+ */
+ void setPreferredNetworkType(int networkType, Message response);
+
+ /**
+ * Query the preferred network type setting
+ *
+ * @param response is callback message to report one of NT_*_TYPE
+ */
+ void getPreferredNetworkType(Message response);
+
+ /**
+ * Gets the default SMSC address.
+ *
+ * @param result Callback message contains the SMSC address.
+ */
+ void getSmscAddress(Message result);
+
+ /**
+ * Sets the default SMSC address.
+ *
+ * @param address new SMSC address
+ * @param result Callback message is empty on completion
+ */
+ void setSmscAddress(String address, Message result);
+
+ /**
+ * Query neighboring cell IDs. response
is dispatched when
+ * this is complete. response.obj
will be an AsyncResult,
+ * and response.obj.exception
will be non-null on failure.
+ * On success, AsyncResult.result
will be a String[]
+ * containing the neighboring cell IDs. Index 0 will contain the count
+ * of available cell IDs. Cell IDs are in hexadecimal format.
+ *
+ * @param response callback message that is dispatched when the query
+ * completes.
+ */
+ void getNeighboringCids(Message response);
+
+ /**
+ * Sets an event to be fired when the telephony system processes
+ * a post-dial character on an outgoing call.
+ *
+ * Messages of type what
will be sent to h
.
+ * The obj
field of these Message's will be instances of
+ * AsyncResult
. Message.obj.result
will be
+ * a Connection object.
+ *
+ * Message.arg1 will be the post dial character being processed,
+ * or 0 ('\0') if end of string.
+ *
+ * If Connection.getPostDialState() == WAIT,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar()
+ * Connection.proceedAfterWaitChar()} or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the post-dial
+ * DTMF sequence.
+ *
+ * If Connection.getPostDialState() == WILD,
+ * the application must call
+ * {@link com.android.internal.telephony.Connection#proceedAfterWildChar
+ * Connection.proceedAfterWildChar()}
+ * or
+ * {@link com.android.internal.telephony.Connection#cancelPostDial()
+ * Connection.cancelPostDial()}
+ * for the telephony system to continue playing the
+ * post-dial DTMF sequence.
+ *
+ * Only one post dial character handler may be set.
+ * Calling this method with "h" equal to null unsets this handler.
+ */
+ void setOnPostDialCharacter(Handler h, int what, Object obj);
+
+
+ /**
+ * Mutes or unmutes the microphone for the active call. The microphone
+ * is automatically unmuted if a call is answered, dialed, or resumed
+ * from a holding state.
+ *
+ * @param muted true to mute the microphone,
+ * false to activate the microphone.
+ */
+
+ void setMute(boolean muted);
+
+ /**
+ * Gets current mute status. Use
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
+ * as a change notifcation, although presently phone state changed is not
+ * fired when setMute() is called.
+ *
+ * @return true is muting, false is unmuting
+ */
+ boolean getMute();
+
+ /**
+ * Enables or disables echo suppression.
+ */
+ void setEchoSuppressionEnabled(boolean enabled);
+
+ /**
+ * Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation.
+ *
+ * @param data The data for the request.
+ * @param response On success,
+ * (byte[])(((AsyncResult)response.obj).result)
+ * On failure,
+ * (((AsyncResult)response.obj).result) == null and
+ * (((AsyncResult)response.obj).exception) being an instance of
+ * com.android.internal.telephony.gsm.CommandException
+ *
+ * @see #invokeOemRilRequestRaw(byte[], android.os.Message)
+ */
+ void invokeOemRilRequestRaw(byte[] data, Message response);
+
+ /**
+ * Invokes RIL_REQUEST_OEM_HOOK_Strings on RIL implementation.
+ *
+ * @param strings The strings to make available as the request data.
+ * @param response On success, "response" bytes is
+ * made available as:
+ * (String[])(((AsyncResult)response.obj).result).
+ * On failure,
+ * (((AsyncResult)response.obj).result) == null and
+ * (((AsyncResult)response.obj).exception) being an instance of
+ * com.android.internal.telephony.gsm.CommandException
+ *
+ * @see #invokeOemRilRequestStrings(java.lang.String[], android.os.Message)
+ */
+ void invokeOemRilRequestStrings(String[] strings, Message response);
+
+ /**
+ * Get the current active Data Call list
+ *
+ * @param response On success, "response" bytes is
+ * made available as:
+ * (String[])(((AsyncResult)response.obj).result).
+ * On failure,
+ * (((AsyncResult)response.obj).result) == null and
+ * (((AsyncResult)response.obj).exception) being an instance of
+ * com.android.internal.telephony.gsm.CommandException
+ */
+ void getDataCallList(Message response);
+
+ /**
+ * Update the ServiceState CellLocation for current network registration.
+ */
+ void updateServiceLocation();
+
+ /**
+ * Enable location update notifications.
+ */
+ void enableLocationUpdates();
+
+ /**
+ * Disable location update notifications.
+ */
+ void disableLocationUpdates();
+
+ /**
+ * For unit tests; don't send notifications to "Phone"
+ * mailbox registrants if true.
+ */
+ void setUnitTestMode(boolean f);
+
+ /**
+ * @return true If unit test mode is enabled
+ */
+ boolean getUnitTestMode();
+
+ /**
+ * Assign a specified band for RF configuration.
+ *
+ * @param bandMode one of BM_*_BAND
+ * @param response is callback message
+ */
+ void setBandMode(int bandMode, Message response);
+
+ /**
+ * Query the list of band mode supported by RF.
+ *
+ * @param response is callback message
+ * ((AsyncResult)response.obj).result is an int[] with every
+ * element representing one avialable BM_*_BAND
+ */
+ void queryAvailableBandMode(Message response);
+
+ /**
+ * @return true if enable data connection on roaming
+ */
+ boolean getDataRoamingEnabled();
+
+ /**
+ * @param enable set true if enable data connection on roaming
+ */
+ void setDataRoamingEnabled(boolean enable);
+
+ /**
+ * Query the CDMA roaming preference setting
+ *
+ * @param response is callback message to report one of CDMA_RM_*
+ */
+ void queryCdmaRoamingPreference(Message response);
+
+ /**
+ * Requests to set the CDMA roaming preference
+ * @param cdmaRoamingType one of CDMA_RM_*
+ * @param response is callback message
+ */
+ void setCdmaRoamingPreference(int cdmaRoamingType, Message response);
+
+ /**
+ * Requests to set the CDMA subscription mode
+ * @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_*
+ * @param response is callback message
+ */
+ void setCdmaSubscription(int cdmaSubscriptionType, Message response);
+
+ /**
+ * If this is a simulated phone interface, returns a SimulatedRadioControl.
+ * @ return A SimulatedRadioControl if this is a simulated interface;
+ * otherwise, null.
+ */
+ SimulatedRadioControl getSimulatedRadioControl();
+
+ /**
+ * Enables the specified APN type. Only works for "special" APN types,
+ * i.e., not the default APN.
+ * @param type The desired APN type. Cannot be {@link #APN_TYPE_DEFAULT}.
+ * @return APN_ALREADY_ACTIVE
if the current APN
+ * services the requested type.
+ * APN_TYPE_NOT_AVAILABLE
if the carrier does not
+ * support the requested APN.
+ * APN_REQUEST_STARTED
if the request has been initiated.
+ * APN_REQUEST_FAILED
if the request was invalid.
+ * A ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
broadcast will
+ * indicate connection state progress.
+ */
+ int enableApnType(String type);
+
+ /**
+ * Disables the specified APN type, and switches back to the default APN,
+ * if necessary. Switching to the default APN will not happen if default
+ * data traffic has been explicitly disabled via a call to {@link #disableDataConnectivity}.
+ *
Only works for "special" APN types,
+ * i.e., not the default APN.
+ * @param type The desired APN type. Cannot be {@link #APN_TYPE_DEFAULT}.
+ * @return APN_ALREADY_ACTIVE
if the default APN
+ * is already active.
+ * APN_REQUEST_STARTED
if the request to switch to the default
+ * APN has been initiated.
+ * APN_REQUEST_FAILED
if the request was invalid.
+ * A ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
broadcast will
+ * indicate connection state progress.
+ */
+ int disableApnType(String type);
+
+ /**
+ * Report on whether data connectivity is allowed.
+ */
+ boolean isDataConnectivityPossible();
+
+ /**
+ * Report on whether data connectivity is allowed for an APN.
+ */
+ boolean isDataConnectivityPossible(String apnType);
+
+ /**
+ * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
+ */
+ String getDeviceId();
+
+ /**
+ * Retrieves the software version number for the device, e.g., IMEI/SV
+ * for GSM phones.
+ */
+ String getDeviceSvn();
+
+ /**
+ * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
+ */
+ String getSubscriberId();
+
+ /**
+ * Retrieves the serial number of the ICC, if applicable.
+ */
+ String getIccSerialNumber();
+
+ /* CDMA support methods */
+
+ /**
+ * Retrieves the MIN for CDMA phones.
+ */
+ String getCdmaMin();
+
+ /**
+ * Check if subscription data has been assigned to mMin
+ *
+ * return true if MIN info is ready; false otherwise.
+ */
+ boolean isMinInfoReady();
+
+ /**
+ * Retrieves PRL Version for CDMA phones
+ */
+ String getCdmaPrlVersion();
+
+ /**
+ * Retrieves the ESN for CDMA phones.
+ */
+ String getEsn();
+
+ /**
+ * Retrieves MEID for CDMA phones.
+ */
+ String getMeid();
+
+ /**
+ * Retrieves the MSISDN from the UICC. For GSM/UMTS phones, this is equivalent to
+ * {@link #getLine1Number()}. For CDMA phones, {@link #getLine1Number()} returns
+ * the MDN, so this method is provided to return the MSISDN on CDMA/LTE phones.
+ */
+ String getMsisdn();
+
+ /**
+ * Retrieves IMEI for phones. Returns null if IMEI is not set.
+ */
+ String getImei();
+
+ /**
+ * Retrieves the PhoneSubInfo of the Phone
+ */
+ public PhoneSubInfo getPhoneSubInfo();
+
+ /**
+ * Retrieves the IccSmsInterfaceManager of the Phone
+ */
+ public IccSmsInterfaceManager getIccSmsInterfaceManager();
+
+ /**
+ * Retrieves the IccPhoneBookInterfaceManager of the Phone
+ */
+ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager();
+
+ /**
+ * setTTYMode
+ * sets a TTY mode option.
+ * @param ttyMode is a one of the following:
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ * @param onComplete a callback message when the action is completed
+ */
+ void setTTYMode(int ttyMode, Message onComplete);
+
+ /**
+ * queryTTYMode
+ * query the status of the TTY mode
+ *
+ * @param onComplete a callback message when the action is completed.
+ */
+ void queryTTYMode(Message onComplete);
+
+ /**
+ * Activate or deactivate cell broadcast SMS.
+ *
+ * @param activate
+ * 0 = activate, 1 = deactivate
+ * @param response
+ * Callback message is empty on completion
+ */
+ void activateCellBroadcastSms(int activate, Message response);
+
+ /**
+ * Query the current configuration of cdma cell broadcast SMS.
+ *
+ * @param response
+ * Callback message is empty on completion
+ */
+ void getCellBroadcastSmsConfig(Message response);
+
+ /**
+ * Configure cell broadcast SMS.
+ *
+ * TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig
+ *
+ * @param response
+ * Callback message is empty on completion
+ */
+ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response);
+
+ public void notifyDataActivity();
+
+ /**
+ * Returns the CDMA ERI icon index to display
+ */
+ public int getCdmaEriIconIndex();
+
+ /**
+ * Returns the CDMA ERI icon mode,
+ * 0 - ON
+ * 1 - FLASHING
+ */
+ public int getCdmaEriIconMode();
+
+ /**
+ * Returns the CDMA ERI text,
+ */
+ public String getCdmaEriText();
+
+ /**
+ * request to exit emergency call back mode
+ * the caller should use setOnECMModeExitResponse
+ * to receive the emergency callback mode exit response
+ */
+ void exitEmergencyCallbackMode();
+
+ /**
+ * this decides if the dial number is OTA(Over the air provision) number or not
+ * @param dialStr is string representing the dialing digit(s)
+ * @return true means the dialStr is OTA number, and false means the dialStr is not OTA number
+ */
+ boolean isOtaSpNumber(String dialStr);
+
+ /**
+ * Returns true if OTA Service Provisioning needs to be performed.
+ */
+ boolean needsOtaServiceProvisioning();
+
+ /**
+ * Register for notifications when CDMA call waiting comes
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForCallWaiting(Handler h, int what, Object obj);
+
+ /**
+ * Unegister for notifications when CDMA Call waiting comes
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForCallWaiting(Handler h);
+
+
+ /**
+ * Register for signal information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+
+ void registerForSignalInfo(Handler h, int what, Object obj) ;
+ /**
+ * Unregisters for signal information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForSignalInfo(Handler h);
+
+ /**
+ * Register for display information notifications from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a SuppServiceNotification instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForDisplayInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for display information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForDisplayInfo(Handler h) ;
+
+ /**
+ * Register for CDMA number information record notification from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a CdmaInformationRecords.CdmaNumberInfoRec
+ * instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForNumberInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for number information record notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForNumberInfo(Handler h);
+
+ /**
+ * Register for CDMA redirected number information record notification
+ * from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a CdmaInformationRecords.CdmaRedirectingNumberInfoRec
+ * instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForRedirectedNumberInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for redirected number information record notification.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForRedirectedNumberInfo(Handler h);
+
+ /**
+ * Register for CDMA line control information record notification
+ * from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a CdmaInformationRecords.CdmaLineControlInfoRec
+ * instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForLineControlInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for line control information notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForLineControlInfo(Handler h);
+
+ /**
+ * Register for CDMA T53 CLIR information record notifications
+ * from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a CdmaInformationRecords.CdmaT53ClirInfoRec
+ * instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerFoT53ClirlInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for T53 CLIR information record notification
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForT53ClirInfo(Handler h);
+
+ /**
+ * Register for CDMA T53 audio control information record notifications
+ * from the network.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a CdmaInformationRecords.CdmaT53AudioControlInfoRec
+ * instance.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ void registerForT53AudioControlInfo(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for T53 audio control information record notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unregisterForT53AudioControlInfo(Handler h);
+
+ /**
+ * registers for exit emergency call back mode request response
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+
+ void setOnEcbModeExitResponse(Handler h, int what, Object obj);
+
+ /**
+ * Unregisters for exit emergency call back mode request response
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ void unsetOnEcbModeExitResponse(Handler h);
+
+ /**
+ * Return if the current radio is LTE on CDMA. This
+ * is a tri-state return value as for a period of time
+ * the mode may be unknown.
+ *
+ * @return {@link #LTE_ON_CDMA_UNKNOWN}, {@link #LTE_ON_CDMA_FALSE} or {@link #LTE_ON_CDMA_TRUE}
+ */
+ public int getLteOnCdmaMode();
+
+ /**
+ * TODO: Adding a function for each property is not good.
+ * A fucntion of type getPhoneProp(propType) where propType is an
+ * enum of GSM+CDMA+LTE props would be a better approach.
+ *
+ * Get "Restriction of menu options for manual PLMN selection" bit
+ * status from EF_CSP data, this belongs to "Value Added Services Group".
+ * @return true if this bit is set or EF_CSP data is unavailable,
+ * false otherwise
+ */
+ boolean isCspPlmnEnabled();
+
+ /**
+ * Return an interface to retrieve the ISIM records for IMS, if available.
+ * @return the interface to retrieve the ISIM records, or null if not supported
+ */
+ IsimRecords getIsimRecords();
+
+ /**
+ * Request the ISIM application on the UICC to perform the AKA
+ * challenge/response algorithm for IMS authentication. The nonce string
+ * and challenge response are Base64 encoded Strings.
+ *
+ * @param nonce the nonce string to pass with the ISIM authentication request
+ * @param response a callback message with the String response in the obj field
+ */
+ void requestIsimAuthentication(String nonce, Message response);
+
+ /**
+ * Sets the SIM voice message waiting indicator records.
+ * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
+ * @param countWaiting The number of messages waiting, if known. Use
+ * -1 to indicate that an unknown number of
+ * messages are waiting
+ */
+ void setVoiceMessageWaiting(int line, int countWaiting);
+
+ /**
+ * Gets the USIM service table from the UICC, if present and available.
+ * @return an interface to the UsimServiceTable record, or null if not available
+ */
+ UsimServiceTable getUsimServiceTable();
+
+ /**
+ * Unregister from all events it registered for and dispose objects
+ * created by this object.
+ */
+ void dispose();
+
+ /**
+ * Remove references to external object stored in this object.
+ */
+ void removeReferences();
+}
diff --git a/src/java/com/android/internal/telephony/PhoneBase.java b/src/java/com/android/internal/telephony/PhoneBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b55240a90647ec8374d0614511505a1b142bd599
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneBase.java
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.SharedPreferences;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telephony.ServiceState;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.telephony.gsm.UsimServiceTable;
+import com.android.internal.telephony.ims.IsimRecords;
+import com.android.internal.telephony.test.SimulatedRadioControl;
+import com.android.internal.telephony.gsm.SIMRecords;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
+
+
+/**
+ * (Not for SDK use)
+ * A base implementation for the com.android.internal.telephony.Phone interface.
+ *
+ * Note that implementations of Phone.java are expected to be used
+ * from a single application thread. This should be the same thread that
+ * originally called PhoneFactory to obtain the interface.
+ *
+ * {@hide}
+ *
+ */
+
+public abstract class PhoneBase extends Handler implements Phone {
+ private static final String LOG_TAG = "PHONE";
+ private static final boolean LOCAL_DEBUG = true;
+
+ // Key used to read and write the saved network selection numeric value
+ 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/write "disable data connection on boot" pref (used for testing)
+ public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key";
+
+ /* Event Constants */
+ protected static final int EVENT_RADIO_AVAILABLE = 1;
+ /** Supplementary Service Notification received. */
+ protected static final int EVENT_SSN = 2;
+ protected static final int EVENT_SIM_RECORDS_LOADED = 3;
+ protected static final int EVENT_MMI_DONE = 4;
+ protected static final int EVENT_RADIO_ON = 5;
+ protected static final int EVENT_GET_BASEBAND_VERSION_DONE = 6;
+ protected static final int EVENT_USSD = 7;
+ protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8;
+ protected static final int EVENT_GET_IMEI_DONE = 9;
+ protected static final int EVENT_GET_IMEISV_DONE = 10;
+ protected static final int EVENT_GET_SIM_STATUS_DONE = 11;
+ protected static final int EVENT_SET_CALL_FORWARD_DONE = 12;
+ protected static final int EVENT_GET_CALL_FORWARD_DONE = 13;
+ protected static final int EVENT_CALL_RING = 14;
+ protected static final int EVENT_CALL_RING_CONTINUE = 15;
+
+ // Used to intercept the carrier selection calls so that
+ // we can save the values.
+ protected static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 16;
+ protected static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 17;
+ protected static final int EVENT_SET_CLIR_COMPLETE = 18;
+ protected static final int EVENT_REGISTERED_TO_NETWORK = 19;
+ protected static final int EVENT_SET_VM_NUMBER_DONE = 20;
+ // Events for CDMA support
+ protected static final int EVENT_GET_DEVICE_IDENTITY_DONE = 21;
+ protected static final int EVENT_RUIM_RECORDS_LOADED = 22;
+ protected static final int EVENT_NV_READY = 23;
+ protected static final int EVENT_SET_ENHANCED_VP = 24;
+ protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 25;
+ protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 26;
+ protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 27;
+ // other
+ protected static final int EVENT_SET_NETWORK_AUTOMATIC = 28;
+ protected static final int EVENT_NEW_ICC_SMS = 29;
+ protected static final int EVENT_ICC_RECORD_EVENTS = 30;
+
+ // Key used to read/write current CLIR setting
+ public static final String CLIR_KEY = "clir_key";
+
+ // Key used to read/write "disable DNS server check" pref (used for testing)
+ public static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
+
+ /* Instance Variables */
+ public CommandsInterface mCM;
+ boolean mDnsCheckDisabled;
+ public DataConnectionTracker mDataConnectionTracker;
+ boolean mDoesRilSendMultipleCallRing;
+ int mCallRingContinueToken;
+ int mCallRingDelay;
+ public boolean mIsTheCurrentActivePhone = true;
+ boolean mIsVoiceCapable = true;
+ public IccRecords mIccRecords;
+ protected AtomicReference mIccCard = new AtomicReference();
+ public SmsStorageMonitor mSmsStorageMonitor;
+ public SmsUsageMonitor mSmsUsageMonitor;
+ public SMSDispatcher mSMS;
+
+ /**
+ * Set a system property, unless we're in unit test mode
+ */
+ public void
+ setSystemProperty(String property, String value) {
+ if(getUnitTestMode()) {
+ return;
+ }
+ SystemProperties.set(property, value);
+ }
+
+
+ protected final RegistrantList mPreciseCallStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mNewRingingConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mIncomingRingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisconnectRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mServiceStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiCompleteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mUnknownConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSuppServiceFailedRegistrants
+ = new RegistrantList();
+
+ protected Looper mLooper; /* to insure registrants are in correct thread*/
+
+ protected final Context mContext;
+
+ /**
+ * PhoneNotifier is an abstraction for all system-wide
+ * state change notification. DefaultPhoneNotifier is
+ * used here unless running we're inside a unit test.
+ */
+ protected PhoneNotifier mNotifier;
+
+ protected SimulatedRadioControl mSimulatedRadioControl;
+
+ boolean mUnitTestMode;
+
+ /**
+ * Constructs a PhoneBase in normal (non-unit test) mode.
+ *
+ * @param context Context object from hosting application
+ * @param notifier An instance of DefaultPhoneNotifier,
+ * unless unit testing.
+ */
+ protected PhoneBase(PhoneNotifier notifier, Context context, CommandsInterface ci) {
+ this(notifier, context, ci, false);
+ }
+
+ /**
+ * Constructs a PhoneBase in normal (non-unit test) mode.
+ *
+ * @param context Context object from hosting application
+ * @param notifier An instance of DefaultPhoneNotifier,
+ * unless unit testing.
+ * @param unitTestMode when true, prevents notifications
+ * of state change events
+ */
+ protected PhoneBase(PhoneNotifier notifier, Context context, CommandsInterface ci,
+ boolean unitTestMode) {
+ this.mNotifier = notifier;
+ this.mContext = context;
+ mLooper = Looper.myLooper();
+ mCM = ci;
+
+ setPropertiesByCarrier();
+
+ setUnitTestMode(unitTestMode);
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false);
+ mCM.setOnCallRing(this, EVENT_CALL_RING, null);
+
+ /* "Voice capable" means that this device supports circuit-switched
+ * (i.e. voice) phone calls over the telephony network, and is allowed
+ * to display the in-call UI while a cellular voice call is active.
+ * This will be false on "data only" devices which can't make voice
+ * calls and don't support any in-call UI.
+ */
+ mIsVoiceCapable = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_capable);
+
+ /**
+ * Some RIL's don't always send RIL_UNSOL_CALL_RING so it needs
+ * to be generated locally. Ideally all ring tones should be loops
+ * and this wouldn't be necessary. But to minimize changes to upper
+ * layers it is requested that it be generated by lower layers.
+ *
+ * By default old phones won't have the property set but do generate
+ * the RIL_UNSOL_CALL_RING so the default if there is no property is
+ * true.
+ */
+ mDoesRilSendMultipleCallRing = SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_RIL_SENDS_MULTIPLE_CALL_RING, true);
+ Log.d(LOG_TAG, "mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
+
+ mCallRingDelay = SystemProperties.getInt(
+ TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
+ Log.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+
+ // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
+ mSmsStorageMonitor = new SmsStorageMonitor(this);
+ mSmsUsageMonitor = new SmsUsageMonitor(context);
+ }
+
+ public void dispose() {
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ mCM.unSetOnCallRing(this);
+ // Must cleanup all connectionS and needs to use sendMessage!
+ mDataConnectionTracker.cleanUpAllConnections(null);
+ mIsTheCurrentActivePhone = false;
+ // Dispose the SMS usage and storage monitors
+ mSmsStorageMonitor.dispose();
+ mSmsUsageMonitor.dispose();
+ }
+ }
+
+ public void removeReferences() {
+ mSmsStorageMonitor = null;
+ mSmsUsageMonitor = null;
+ mSMS = null;
+ mIccRecords = null;
+ mIccCard.set(null);
+ mDataConnectionTracker = null;
+ }
+
+ /**
+ * When overridden the derived class needs to call
+ * super.handleMessage(msg) so this method has a
+ * a chance to process the message.
+ *
+ * @param msg
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case EVENT_CALL_RING:
+ Log.d(LOG_TAG, "Event EVENT_CALL_RING Received state=" + getState());
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ PhoneConstants.State state = getState();
+ if ((!mDoesRilSendMultipleCallRing)
+ && ((state == PhoneConstants.State.RINGING) ||
+ (state == PhoneConstants.State.IDLE))) {
+ mCallRingContinueToken += 1;
+ sendIncomingCallRingNotification(mCallRingContinueToken);
+ } else {
+ notifyIncomingRing();
+ }
+ }
+ break;
+
+ case EVENT_CALL_RING_CONTINUE:
+ Log.d(LOG_TAG, "Event EVENT_CALL_RING_CONTINUE Received stat=" + getState());
+ if (getState() == PhoneConstants.State.RINGING) {
+ sendIncomingCallRingNotification(msg.arg1);
+ }
+ break;
+
+ default:
+ throw new RuntimeException("unexpected event not handled");
+ }
+ }
+
+ // Inherited documentation suffices.
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Disables the DNS check (i.e., allows "0.0.0.0").
+ * Useful for lab testing environment.
+ * @param b true disables the check, false enables.
+ */
+ public void disableDnsCheck(boolean b) {
+ mDnsCheckDisabled = b;
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b);
+ editor.apply();
+ }
+
+ /**
+ * Returns true if the DNS check is currently disabled.
+ */
+ public boolean isDnsCheckDisabled() {
+ return mDnsCheckDisabled;
+ }
+
+ // Inherited documentation suffices.
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mPreciseCallStateRegistrants.remove(h);
+ }
+
+ /**
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ protected void notifyPreciseCallStateChangedP() {
+ AsyncResult ar = new AsyncResult(null, this, null);
+ mPreciseCallStateRegistrants.notifyRegistrants(ar);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForUnknownConnection(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mUnknownConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForUnknownConnection(Handler h) {
+ mUnknownConnectionRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForNewRingingConnection(
+ Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mNewRingingConnectionRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForNewRingingConnection(Handler h) {
+ mNewRingingConnectionRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
+ mCM.registerForInCallVoicePrivacyOn(h,what,obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mCM.unregisterForInCallVoicePrivacyOn(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
+ mCM.registerForInCallVoicePrivacyOff(h,what,obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mCM.unregisterForInCallVoicePrivacyOff(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForIncomingRing(
+ Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mIncomingRingRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForIncomingRing(Handler h) {
+ mIncomingRingRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mDisconnectRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForDisconnect(Handler h) {
+ mDisconnectRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mSuppServiceFailedRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForSuppServiceFailed(Handler h) {
+ mSuppServiceFailedRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForMmiInitiate(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mMmiRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForMmiInitiate(Handler h) {
+ mMmiRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForMmiComplete(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mMmiCompleteRegistrants.addUnique(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForMmiComplete(Handler h) {
+ checkCorrectThread(h);
+
+ mMmiCompleteRegistrants.remove(h);
+ }
+
+ /**
+ * Method to retrieve the saved operator id from the Shared Preferences
+ */
+ private String getSavedNetworkSelection() {
+ // open the shared preferences and search with our key.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return sp.getString(NETWORK_SELECTION_KEY, "");
+ }
+
+ /**
+ * Method to restore the previously saved operator id, or reset to
+ * automatic selection, all depending upon the value in the shared
+ * preferences.
+ */
+ public void restoreSavedNetworkSelection(Message response) {
+ // retrieve the operator id
+ String networkSelection = getSavedNetworkSelection();
+
+ // set to auto if the id is empty, otherwise select the network.
+ if (TextUtils.isEmpty(networkSelection)) {
+ mCM.setNetworkSelectionModeAutomatic(response);
+ } else {
+ mCM.setNetworkSelectionModeManual(networkSelection, response);
+ }
+ }
+
+ // Inherited documentation suffices.
+ public void setUnitTestMode(boolean f) {
+ mUnitTestMode = f;
+ }
+
+ // Inherited documentation suffices.
+ public boolean getUnitTestMode() {
+ return mUnitTestMode;
+ }
+
+ /**
+ * To be invoked when a voice call Connection disconnects.
+ *
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ protected void notifyDisconnectP(Connection cn) {
+ AsyncResult ar = new AsyncResult(null, cn, null);
+ mDisconnectRegistrants.notifyRegistrants(ar);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForServiceStateChanged(
+ Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+
+ mServiceStateRegistrants.add(h, what, obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForServiceStateChanged(Handler h) {
+ mServiceStateRegistrants.remove(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForRingbackTone(Handler h, int what, Object obj) {
+ mCM.registerForRingbackTone(h,what,obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForRingbackTone(Handler h) {
+ mCM.unregisterForRingbackTone(h);
+ }
+
+ // Inherited documentation suffices.
+ public void registerForResendIncallMute(Handler h, int what, Object obj) {
+ mCM.registerForResendIncallMute(h,what,obj);
+ }
+
+ // Inherited documentation suffices.
+ public void unregisterForResendIncallMute(Handler h) {
+ mCM.unregisterForResendIncallMute(h);
+ }
+
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ // no need for regular phone
+ }
+
+ /**
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ protected void notifyServiceStateChangedP(ServiceState ss) {
+ AsyncResult ar = new AsyncResult(null, ss, null);
+ mServiceStateRegistrants.notifyRegistrants(ar);
+
+ mNotifier.notifyServiceState(this);
+ }
+
+ // Inherited documentation suffices.
+ public SimulatedRadioControl getSimulatedRadioControl() {
+ return mSimulatedRadioControl;
+ }
+
+ /**
+ * Verifies the current thread is the same as the thread originally
+ * used in the initialization of this instance. Throws RuntimeException
+ * if not.
+ *
+ * @exception RuntimeException if the current thread is not
+ * the thread that originally obtained this PhoneBase instance.
+ */
+ private void checkCorrectThread(Handler h) {
+ if (h.getLooper() != mLooper) {
+ throw new RuntimeException(
+ "com.android.internal.telephony.Phone must be used from within one thread");
+ }
+ }
+
+ /**
+ * Set the properties by matching the carrier string in
+ * a string-array resource
+ */
+ private void setPropertiesByCarrier() {
+ String carrier = SystemProperties.get("ro.carrier");
+
+ if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) {
+ return;
+ }
+
+ CharSequence[] carrierLocales = mContext.
+ getResources().getTextArray(R.array.carrier_properties);
+
+ for (int i = 0; i < carrierLocales.length; i+=3) {
+ String c = carrierLocales[i].toString();
+ if (carrier.equals(c)) {
+ String l = carrierLocales[i+1].toString();
+
+ String language = l.substring(0, 2);
+ String country = "";
+ if (l.length() >=5) {
+ country = l.substring(3, 5);
+ }
+ MccTable.setSystemLocale(mContext, language, country);
+
+ if (!country.isEmpty()) {
+ try {
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.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;
+ }
+ }
+ }
+
+ /**
+ * Get state
+ */
+ public abstract PhoneConstants.State getState();
+
+ /**
+ * Retrieves the IccFileHandler of the Phone instance
+ */
+ public IccFileHandler getIccFileHandler(){
+ IccCard iccCard = mIccCard.get();
+ if (iccCard == null) return null;
+ return iccCard.getIccFileHandler();
+ }
+
+ /*
+ * Retrieves the Handler of the Phone instance
+ */
+ public Handler getHandler() {
+ return this;
+ }
+
+ /**
+ * Retrieves the ServiceStateTracker of the phone instance.
+ */
+ public ServiceStateTracker getServiceStateTracker() {
+ return null;
+ }
+
+ /**
+ * Get call tracker
+ */
+ public CallTracker getCallTracker() {
+ return null;
+ }
+
+ @Override
+ public IccCard getIccCard() {
+ return mIccCard.get();
+ }
+
+ @Override
+ public String getIccSerialNumber() {
+ return mIccRecords.iccid;
+ }
+
+ @Override
+ public boolean getIccRecordsLoaded() {
+ return mIccRecords.getRecordsLoaded();
+ }
+
+ @Override
+ public boolean getMessageWaitingIndicator() {
+ return mIccRecords.getVoiceMessageWaiting();
+ }
+
+ @Override
+ public boolean getCallForwardingIndicator() {
+ return mIccRecords.getVoiceCallForwardingFlag();
+ }
+
+ /**
+ * Query the status of the CDMA roaming preference
+ */
+ public void queryCdmaRoamingPreference(Message response) {
+ mCM.queryCdmaRoamingPreference(response);
+ }
+
+ /**
+ * Set the status of the CDMA roaming preference
+ */
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ mCM.setCdmaRoamingPreference(cdmaRoamingType, response);
+ }
+
+ /**
+ * Set the status of the CDMA subscription mode
+ */
+ public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
+ mCM.setCdmaSubscriptionSource(cdmaSubscriptionType, response);
+ }
+
+ /**
+ * Set the preferred Network Type: Global, CDMA only or GSM/UMTS only
+ */
+ public void setPreferredNetworkType(int networkType, Message response) {
+ mCM.setPreferredNetworkType(networkType, response);
+ }
+
+ public void getPreferredNetworkType(Message response) {
+ mCM.getPreferredNetworkType(response);
+ }
+
+ public void getSmscAddress(Message result) {
+ mCM.getSmscAddress(result);
+ }
+
+ public void setSmscAddress(String address, Message result) {
+ mCM.setSmscAddress(address, result);
+ }
+
+ public void setTTYMode(int ttyMode, Message onComplete) {
+ mCM.setTTYMode(ttyMode, onComplete);
+ }
+
+ public void queryTTYMode(Message onComplete) {
+ mCM.queryTTYMode(onComplete);
+ }
+
+ public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("enableEnhancedVoicePrivacy");
+ }
+
+ public void getEnhancedVoicePrivacy(Message onComplete) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("getEnhancedVoicePrivacy");
+ }
+
+ public void setBandMode(int bandMode, Message response) {
+ mCM.setBandMode(bandMode, response);
+ }
+
+ public void queryAvailableBandMode(Message response) {
+ mCM.queryAvailableBandMode(response);
+ }
+
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ mCM.invokeOemRilRequestRaw(data, response);
+ }
+
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ mCM.invokeOemRilRequestStrings(strings, response);
+ }
+
+ public void notifyDataActivity() {
+ mNotifier.notifyDataActivity(this);
+ }
+
+ public void notifyMessageWaitingIndicator() {
+ // Do not notify voice mail waiting if device doesn't support voice
+ if (!mIsVoiceCapable)
+ return;
+
+ // This function is added to send the notification to DefaultPhoneNotifier.
+ mNotifier.notifyMessageWaitingChanged(this);
+ }
+
+ public void notifyDataConnection(String reason, String apnType,
+ PhoneConstants.DataState state) {
+ mNotifier.notifyDataConnection(this, reason, apnType, state);
+ }
+
+ public void notifyDataConnection(String reason, String apnType) {
+ mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
+ }
+
+ public void notifyDataConnection(String reason) {
+ String types[] = getActiveApnTypes();
+ for (String apnType : types) {
+ mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
+ }
+ }
+
+ public void notifyOtaspChanged(int otaspMode) {
+ mNotifier.notifyOtaspChanged(this, otaspMode);
+ }
+
+ /**
+ * @return true if a mobile originating emergency call is active
+ */
+ public boolean isInEmergencyCall() {
+ return false;
+ }
+
+ /**
+ * @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.
+ */
+ public boolean isInEcm() {
+ return false;
+ }
+
+ public abstract String getPhoneName();
+
+ public abstract int getPhoneType();
+
+ /** @hide */
+ public int getVoiceMessageCount(){
+ return 0;
+ }
+
+ /**
+ * Returns the CDMA ERI icon index to display
+ */
+ public int getCdmaEriIconIndex() {
+ logUnexpectedCdmaMethodCall("getCdmaEriIconIndex");
+ return -1;
+ }
+
+ /**
+ * Returns the CDMA ERI icon mode,
+ * 0 - ON
+ * 1 - FLASHING
+ */
+ public int getCdmaEriIconMode() {
+ logUnexpectedCdmaMethodCall("getCdmaEriIconMode");
+ return -1;
+ }
+
+ /**
+ * Returns the CDMA ERI text,
+ */
+ public String getCdmaEriText() {
+ logUnexpectedCdmaMethodCall("getCdmaEriText");
+ return "GSM nw, no ERI";
+ }
+
+ public String getCdmaMin() {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("getCdmaMin");
+ return null;
+ }
+
+ public boolean isMinInfoReady() {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("isMinInfoReady");
+ return false;
+ }
+
+ public String getCdmaPrlVersion(){
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("getCdmaPrlVersion");
+ return null;
+ }
+
+ public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("sendBurstDtmf");
+ }
+
+ public void exitEmergencyCallbackMode() {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("exitEmergencyCallbackMode");
+ }
+
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("registerForCdmaOtaStatusChange");
+ }
+
+ public void unregisterForCdmaOtaStatusChange(Handler h) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("unregisterForCdmaOtaStatusChange");
+ }
+
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("registerForSubscriptionInfoReady");
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("unregisterForSubscriptionInfoReady");
+ }
+
+ /**
+ * Returns true if OTA Service Provisioning needs to be performed.
+ * If not overridden return false.
+ */
+ public boolean needsOtaServiceProvisioning() {
+ return false;
+ }
+
+ /**
+ * Return true if number is an OTASP number.
+ * If not overridden return false.
+ */
+ public boolean isOtaSpNumber(String dialStr) {
+ return false;
+ }
+
+ public void registerForCallWaiting(Handler h, int what, Object obj){
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("registerForCallWaiting");
+ }
+
+ public void unregisterForCallWaiting(Handler h){
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("unregisterForCallWaiting");
+ }
+
+ public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("registerForEcmTimerReset");
+ }
+
+ public void unregisterForEcmTimerReset(Handler h) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("unregisterForEcmTimerReset");
+ }
+
+ public void registerForSignalInfo(Handler h, int what, Object obj) {
+ mCM.registerForSignalInfo(h, what, obj);
+ }
+
+ public void unregisterForSignalInfo(Handler h) {
+ mCM.unregisterForSignalInfo(h);
+ }
+
+ public void registerForDisplayInfo(Handler h, int what, Object obj) {
+ mCM.registerForDisplayInfo(h, what, obj);
+ }
+
+ public void unregisterForDisplayInfo(Handler h) {
+ mCM.unregisterForDisplayInfo(h);
+ }
+
+ public void registerForNumberInfo(Handler h, int what, Object obj) {
+ mCM.registerForNumberInfo(h, what, obj);
+ }
+
+ public void unregisterForNumberInfo(Handler h) {
+ mCM.unregisterForNumberInfo(h);
+ }
+
+ public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) {
+ mCM.registerForRedirectedNumberInfo(h, what, obj);
+ }
+
+ public void unregisterForRedirectedNumberInfo(Handler h) {
+ mCM.unregisterForRedirectedNumberInfo(h);
+ }
+
+ public void registerForLineControlInfo(Handler h, int what, Object obj) {
+ mCM.registerForLineControlInfo( h, what, obj);
+ }
+
+ public void unregisterForLineControlInfo(Handler h) {
+ mCM.unregisterForLineControlInfo(h);
+ }
+
+ public void registerFoT53ClirlInfo(Handler h, int what, Object obj) {
+ mCM.registerFoT53ClirlInfo(h, what, obj);
+ }
+
+ public void unregisterForT53ClirInfo(Handler h) {
+ mCM.unregisterForT53ClirInfo(h);
+ }
+
+ public void registerForT53AudioControlInfo(Handler h, int what, Object obj) {
+ mCM.registerForT53AudioControlInfo( h, what, obj);
+ }
+
+ public void unregisterForT53AudioControlInfo(Handler h) {
+ mCM.unregisterForT53AudioControlInfo(h);
+ }
+
+ public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("setOnEcbModeExitResponse");
+ }
+
+ public void unsetOnEcbModeExitResponse(Handler h){
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
+ }
+
+ public String[] getActiveApnTypes() {
+ return mDataConnectionTracker.getActiveApnTypes();
+ }
+
+ public String getActiveApnHost(String apnType) {
+ return mDataConnectionTracker.getActiveApnString(apnType);
+ }
+
+ public LinkProperties getLinkProperties(String apnType) {
+ return mDataConnectionTracker.getLinkProperties(apnType);
+ }
+
+ public LinkCapabilities getLinkCapabilities(String apnType) {
+ return mDataConnectionTracker.getLinkCapabilities(apnType);
+ }
+
+ public int enableApnType(String type) {
+ return mDataConnectionTracker.enableApnType(type);
+ }
+
+ public int disableApnType(String type) {
+ return mDataConnectionTracker.disableApnType(type);
+ }
+
+ public boolean isDataConnectivityPossible() {
+ return isDataConnectivityPossible(PhoneConstants.APN_TYPE_DEFAULT);
+ }
+
+ public boolean isDataConnectivityPossible(String apnType) {
+ return ((mDataConnectionTracker != null) &&
+ (mDataConnectionTracker.isDataPossible(apnType)));
+ }
+
+ /**
+ * Notify registrants of a new ringing Connection.
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ protected void notifyNewRingingConnectionP(Connection cn) {
+ if (!mIsVoiceCapable)
+ return;
+ AsyncResult ar = new AsyncResult(null, cn, null);
+ mNewRingingConnectionRegistrants.notifyRegistrants(ar);
+ }
+
+ /**
+ * Notify registrants of a RING event.
+ */
+ private void notifyIncomingRing() {
+ if (!mIsVoiceCapable)
+ return;
+ AsyncResult ar = new AsyncResult(null, this, null);
+ mIncomingRingRegistrants.notifyRegistrants(ar);
+ }
+
+ /**
+ * Send the incoming call Ring notification if conditions are right.
+ */
+ private void sendIncomingCallRingNotification(int token) {
+ if (mIsVoiceCapable && !mDoesRilSendMultipleCallRing &&
+ (token == mCallRingContinueToken)) {
+ Log.d(LOG_TAG, "Sending notifyIncomingRing");
+ notifyIncomingRing();
+ sendMessageDelayed(
+ obtainMessage(EVENT_CALL_RING_CONTINUE, token, 0), mCallRingDelay);
+ } else {
+ Log.d(LOG_TAG, "Ignoring ring notification request,"
+ + " mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing
+ + " token=" + token
+ + " mCallRingContinueToken=" + mCallRingContinueToken
+ + " mIsVoiceCapable=" + mIsVoiceCapable);
+ }
+ }
+
+ public boolean isCspPlmnEnabled() {
+ // This function should be overridden by the class GSMPhone.
+ // Not implemented in CDMAPhone.
+ logUnexpectedGsmMethodCall("isCspPlmnEnabled");
+ return false;
+ }
+
+ public IsimRecords getIsimRecords() {
+ Log.e(LOG_TAG, "getIsimRecords() is only supported on LTE devices");
+ return null;
+ }
+
+ public void requestIsimAuthentication(String nonce, Message result) {
+ Log.e(LOG_TAG, "requestIsimAuthentication() is only supported on LTE devices");
+ }
+
+ public String getMsisdn() {
+ logUnexpectedGsmMethodCall("getMsisdn");
+ return null;
+ }
+
+ /**
+ * Common error logger method for unexpected calls to CDMA-only methods.
+ */
+ private static void logUnexpectedCdmaMethodCall(String name)
+ {
+ Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
+ "called, CDMAPhone inactive.");
+ }
+
+ public PhoneConstants.DataState getDataConnectionState() {
+ return getDataConnectionState(PhoneConstants.APN_TYPE_DEFAULT);
+ }
+
+ /**
+ * Common error logger method for unexpected calls to GSM/WCDMA-only methods.
+ */
+ private static void logUnexpectedGsmMethodCall(String name) {
+ Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
+ "called, GSMPhone inactive.");
+ }
+
+ // Called by SimRecords which is constructed with a PhoneBase instead of a GSMPhone.
+ public void notifyCallForwardingIndicator() {
+ // This function should be overridden by the class GSMPhone. Not implemented in CDMAPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ }
+
+ public void notifyDataConnectionFailed(String reason, String apnType) {
+ mNotifier.notifyDataConnectionFailed(this, reason, apnType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getLteOnCdmaMode() {
+ return mCM.getLteOnCdmaMode();
+ }
+
+ /**
+ * Sets the SIM voice message waiting indicator records.
+ * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
+ * @param countWaiting The number of messages waiting, if known. Use
+ * -1 to indicate that an unknown number of
+ * messages are waiting
+ */
+ @Override
+ public void setVoiceMessageWaiting(int line, int countWaiting) {
+ mIccRecords.setVoiceMessageWaiting(line, countWaiting);
+ }
+
+ /**
+ * Gets the USIM service table from the UICC, if present and available.
+ * @return an interface to the UsimServiceTable record, or null if not available
+ */
+ @Override
+ public UsimServiceTable getUsimServiceTable() {
+ return mIccRecords.getUsimServiceTable();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("PhoneBase:");
+ pw.println(" mCM=" + mCM);
+ pw.println(" mDnsCheckDisabled=" + mDnsCheckDisabled);
+ pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
+ pw.println(" mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
+ pw.println(" mCallRingContinueToken=" + mCallRingContinueToken);
+ pw.println(" mCallRingDelay=" + mCallRingDelay);
+ pw.println(" mIsTheCurrentActivePhone=" + mIsTheCurrentActivePhone);
+ pw.println(" mIsVoiceCapable=" + mIsVoiceCapable);
+ pw.println(" mIccRecords=" + mIccRecords);
+ pw.println(" mIccCard=" + mIccCard.get());
+ pw.println(" mSmsStorageMonitor=" + mSmsStorageMonitor);
+ pw.println(" mSmsUsageMonitor=" + mSmsUsageMonitor);
+ pw.println(" mSMS=" + mSMS);
+ pw.flush();
+ pw.println(" mLooper=" + mLooper);
+ pw.println(" mContext=" + mContext);
+ pw.println(" mNotifier=" + mNotifier);
+ pw.println(" mSimulatedRadioControl=" + mSimulatedRadioControl);
+ pw.println(" mUnitTestMode=" + mUnitTestMode);
+ pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
+ pw.println(" getUnitTestMode()=" + getUnitTestMode());
+ pw.println(" getState()=" + getState());
+ pw.println(" getIccSerialNumber()=" + getIccSerialNumber());
+ pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
+ pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
+ pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
+ pw.println(" isInEmergencyCall()=" + isInEmergencyCall());
+ pw.flush();
+ pw.println(" isInEcm()=" + isInEcm());
+ pw.println(" getPhoneName()=" + getPhoneName());
+ pw.println(" getPhoneType()=" + getPhoneType());
+ pw.println(" getVoiceMessageCount()=" + getVoiceMessageCount());
+ pw.println(" getActiveApnTypes()=" + getActiveApnTypes());
+ pw.println(" isDataConnectivityPossible()=" + isDataConnectivityPossible());
+ pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c85dc62af5a1afd8212a8a156c449c1f561cfad
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -0,0 +1,213 @@
+/*
+ * 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.content.Context;
+import android.net.LocalServerSocket;
+import android.os.Looper;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.os.SystemProperties;
+
+import com.android.internal.telephony.cdma.CDMAPhone;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
+import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.sip.SipPhone;
+import com.android.internal.telephony.sip.SipPhoneFactory;
+
+/**
+ * {@hide}
+ */
+public class PhoneFactory {
+ static final String LOG_TAG = "PHONE";
+ static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
+ static final int SOCKET_OPEN_MAX_RETRY = 3;
+
+ //***** Class Variables
+
+ static private Phone sProxyPhone = null;
+ static private CommandsInterface sCommandsInterface = null;
+
+ static private boolean sMadeDefaults = false;
+ static private PhoneNotifier sPhoneNotifier;
+ static private Looper sLooper;
+ static private Context sContext;
+
+ static final int preferredCdmaSubscription =
+ CdmaSubscriptionSourceManager.PREFERRED_CDMA_SUBSCRIPTION;
+
+ //***** Class Methods
+
+ public static void makeDefaultPhones(Context context) {
+ makeDefaultPhone(context);
+ }
+
+ /**
+ * FIXME replace this with some other way of making these
+ * instances
+ */
+ public static void makeDefaultPhone(Context context) {
+ synchronized(Phone.class) {
+ if (!sMadeDefaults) {
+ sLooper = Looper.myLooper();
+ sContext = context;
+
+ if (sLooper == null) {
+ throw new RuntimeException(
+ "PhoneFactory.makeDefaultPhone must be called from Looper thread");
+ }
+
+ int retryCount = 0;
+ for(;;) {
+ boolean hasException = false;
+ retryCount ++;
+
+ try {
+ // use UNIX domain socket to
+ // prevent subsequent initialization
+ new LocalServerSocket("com.android.internal.telephony");
+ } catch (java.io.IOException ex) {
+ hasException = true;
+ }
+
+ if ( !hasException ) {
+ break;
+ } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
+ throw new RuntimeException("PhoneFactory probably already running");
+ } else {
+ try {
+ Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
+ } catch (InterruptedException er) {
+ }
+ }
+ }
+
+ sPhoneNotifier = new DefaultPhoneNotifier();
+
+ // Get preferred network mode
+ int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
+ if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ preferredNetworkMode = Phone.NT_MODE_GLOBAL;
+ }
+ int networkMode = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);
+ Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));
+
+ // Get cdmaSubscription
+ // TODO: Change when the ril will provides a way to know at runtime
+ // the configuration, bug 4202572. And the ril issues the
+ // RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439.
+ int cdmaSubscription;
+ int lteOnCdma = TelephonyManager.getLteOnCdmaModeStatic();
+ switch (lteOnCdma) {
+ case PhoneConstants.LTE_ON_CDMA_FALSE:
+ cdmaSubscription = CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_NV;
+ Log.i(LOG_TAG, "lteOnCdma is 0 use SUBSCRIPTION_FROM_NV");
+ break;
+ case PhoneConstants.LTE_ON_CDMA_TRUE:
+ cdmaSubscription = CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM;
+ Log.i(LOG_TAG, "lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM");
+ break;
+ case PhoneConstants.LTE_ON_CDMA_UNKNOWN:
+ default:
+ //Get cdmaSubscription mode from Settings.System
+ cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
+ preferredCdmaSubscription);
+ Log.i(LOG_TAG, "lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION");
+ break;
+ }
+ Log.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
+
+ //reads the system properties and makes commandsinterface
+ sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
+
+ int phoneType = TelephonyManager.getPhoneType(networkMode);
+ if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ Log.i(LOG_TAG, "Creating GSMPhone");
+ sProxyPhone = new PhoneProxy(new GSMPhone(context,
+ sCommandsInterface, sPhoneNotifier));
+ } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+ switch (TelephonyManager.getLteOnCdmaModeStatic()) {
+ case PhoneConstants.LTE_ON_CDMA_TRUE:
+ Log.i(LOG_TAG, "Creating CDMALTEPhone");
+ sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
+ sCommandsInterface, sPhoneNotifier));
+ break;
+ case PhoneConstants.LTE_ON_CDMA_FALSE:
+ default:
+ Log.i(LOG_TAG, "Creating CDMAPhone");
+ sProxyPhone = new PhoneProxy(new CDMAPhone(context,
+ sCommandsInterface, sPhoneNotifier));
+ break;
+ }
+ }
+
+ sMadeDefaults = true;
+ }
+ }
+ }
+
+ public static Phone getDefaultPhone() {
+ if (sLooper != Looper.myLooper()) {
+ throw new RuntimeException(
+ "PhoneFactory.getDefaultPhone must be called from Looper thread");
+ }
+
+ if (!sMadeDefaults) {
+ throw new IllegalStateException("Default phones haven't been made yet!");
+ }
+ return sProxyPhone;
+ }
+
+ public static Phone getCdmaPhone() {
+ Phone phone;
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ switch (TelephonyManager.getLteOnCdmaModeStatic()) {
+ case PhoneConstants.LTE_ON_CDMA_TRUE: {
+ phone = new CDMALTEPhone(sContext, sCommandsInterface, sPhoneNotifier);
+ break;
+ }
+ case PhoneConstants.LTE_ON_CDMA_FALSE:
+ case PhoneConstants.LTE_ON_CDMA_UNKNOWN:
+ default: {
+ phone = new CDMAPhone(sContext, sCommandsInterface, sPhoneNotifier);
+ break;
+ }
+ }
+ }
+ return phone;
+ }
+
+ public static Phone getGsmPhone() {
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ Phone phone = new GSMPhone(sContext, sCommandsInterface, sPhoneNotifier);
+ return phone;
+ }
+ }
+
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makeSipPhone(String sipUri) {
+ return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..efc7a1347733359feccd92b9f1d120daafa83fd0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -0,0 +1,50 @@
+/*
+ * 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.telephony.CellInfo;
+
+/**
+ * {@hide}
+ */
+public interface PhoneNotifier {
+
+ public void notifyPhoneState(Phone sender);
+
+ public void notifyServiceState(Phone sender);
+
+ public void notifyCellLocation(Phone sender);
+
+ public void notifySignalStrength(Phone sender);
+
+ public void notifyMessageWaitingChanged(Phone sender);
+
+ public void notifyCallForwardingChanged(Phone sender);
+
+ /** TODO - reason should never be null */
+ public void notifyDataConnection(Phone sender, String reason, String apnType,
+ PhoneConstants.DataState state);
+
+ public void notifyDataConnectionFailed(Phone sender, String reason, String apnType);
+
+ public void notifyDataActivity(Phone sender);
+
+ public void notifyOtaspChanged(Phone sender, int otaspMode);
+
+ // TODO - trigger notifyCellInfo from ServiceStateTracker
+ public void notifyCellInfo(Phone sender, CellInfo cellInfo);
+}
diff --git a/src/java/com/android/internal/telephony/PhoneProxy.java b/src/java/com/android/internal/telephony/PhoneProxy.java
new file mode 100644
index 0000000000000000000000000000000000000000..77135d47d4478bfb7fda154625fd99ec47069fea
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneProxy.java
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.telephony.CellLocation;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.util.Log;
+
+import com.android.internal.telephony.cdma.CDMAPhone;
+import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.gsm.UsimServiceTable;
+import com.android.internal.telephony.ims.IsimRecords;
+import com.android.internal.telephony.test.SimulatedRadioControl;
+import com.android.internal.telephony.CallManager;
+
+import java.util.List;
+
+public class PhoneProxy extends Handler implements Phone {
+ public final static Object lockForRadioTechnologyChange = new Object();
+
+ private Phone mActivePhone;
+ private CommandsInterface mCommandsInterface;
+ private IccSmsInterfaceManagerProxy mIccSmsInterfaceManagerProxy;
+ private IccPhoneBookInterfaceManagerProxy mIccPhoneBookInterfaceManagerProxy;
+ private PhoneSubInfoProxy mPhoneSubInfoProxy;
+
+ private boolean mResetModemOnRadioTechnologyChange = false;
+
+ private int mRilVersion;
+
+ private static final int EVENT_VOICE_RADIO_TECH_CHANGED = 1;
+ private static final int EVENT_RADIO_ON = 2;
+ private static final int EVENT_REQUEST_VOICE_RADIO_TECH_DONE = 3;
+ private static final int EVENT_RIL_CONNECTED = 4;
+
+ private static final String LOG_TAG = "PHONE";
+
+ //***** Class Methods
+ public PhoneProxy(Phone phone) {
+ mActivePhone = phone;
+ mResetModemOnRadioTechnologyChange = SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_RESET_ON_RADIO_TECH_CHANGE, false);
+ mIccSmsInterfaceManagerProxy = new IccSmsInterfaceManagerProxy(
+ phone.getIccSmsInterfaceManager());
+ mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(
+ phone.getIccPhoneBookInterfaceManager());
+ mPhoneSubInfoProxy = new PhoneSubInfoProxy(phone.getPhoneSubInfo());
+ mCommandsInterface = ((PhoneBase)mActivePhone).mCM;
+
+ mCommandsInterface.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
+ mCommandsInterface.registerForOn(this, EVENT_RADIO_ON, null);
+ mCommandsInterface.registerForVoiceRadioTechChanged(
+ this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ switch(msg.what) {
+ case EVENT_RADIO_ON:
+ /* Proactively query voice radio technologies */
+ mCommandsInterface.getVoiceRadioTechnology(
+ this.obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE));
+ break;
+
+ case EVENT_RIL_CONNECTED:
+ if (ar.exception == null && ar.result != null) {
+ mRilVersion = (Integer) ar.result;
+ } else {
+ logd("Unexpected exception on EVENT_RIL_CONNECTED");
+ mRilVersion = -1;
+ }
+ break;
+
+ case EVENT_VOICE_RADIO_TECH_CHANGED:
+ case EVENT_REQUEST_VOICE_RADIO_TECH_DONE:
+
+ if (ar.exception == null) {
+ if ((ar.result != null) && (((int[]) ar.result).length != 0)) {
+ int newVoiceTech = ((int[]) ar.result)[0];
+ updatePhoneObject(newVoiceTech);
+ } else {
+ loge("Voice Radio Technology event " + msg.what + " has no tech!");
+ }
+ } else {
+ loge("Voice Radio Technology event " + msg.what + " exception!" + ar.exception);
+ }
+ break;
+
+ default:
+ loge("Error! This handler was not registered for this message type. Message: "
+ + msg.what);
+ break;
+ }
+ super.handleMessage(msg);
+ }
+
+ private static void logd(String msg) {
+ Log.d(LOG_TAG, "[PhoneProxy] " + msg);
+ }
+
+ private void logw(String msg) {
+ Log.w(LOG_TAG, "[PhoneProxy] " + msg);
+ }
+
+ private void loge(String msg) {
+ Log.e(LOG_TAG, "[PhoneProxy] " + msg);
+ }
+
+ private void updatePhoneObject(int newVoiceRadioTech) {
+
+ if (mActivePhone != null) {
+ if(mRilVersion == 6 && getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ /*
+ * On v6 RIL, when LTE_ON_CDMA is TRUE, always create CDMALTEPhone
+ * irrespective of the voice radio tech reported.
+ */
+ if (mActivePhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+ logd("LTE ON CDMA property is set. Use CDMA Phone" +
+ " newVoiceRadioTech = " + newVoiceRadioTech +
+ " Active Phone = " + mActivePhone.getPhoneName());
+ return;
+ } else {
+ logd("LTE ON CDMA property is set. Switch to CDMALTEPhone" +
+ " newVoiceRadioTech = " + newVoiceRadioTech +
+ " Active Phone = " + mActivePhone.getPhoneName());
+ newVoiceRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+ }
+ } else {
+ if ((ServiceState.isCdma(newVoiceRadioTech) &&
+ mActivePhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) ||
+ (ServiceState.isGsm(newVoiceRadioTech) &&
+ mActivePhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)) {
+ // Nothing changed. Keep phone as it is.
+ logd("Ignoring voice radio technology changed message." +
+ " newVoiceRadioTech = " + newVoiceRadioTech +
+ " Active Phone = " + mActivePhone.getPhoneName());
+ return;
+ }
+ }
+ }
+
+ if (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+ // We need some voice phone object to be active always, so never
+ // delete the phone without anything to replace it with!
+ logd("Ignoring voice radio technology changed message. newVoiceRadioTech = Unknown."
+ + " Active Phone = " + mActivePhone.getPhoneName());
+ return;
+ }
+
+ boolean oldPowerState = false; // old power state to off
+ if (mResetModemOnRadioTechnologyChange) {
+ if (mCommandsInterface.getRadioState().isOn()) {
+ oldPowerState = true;
+ logd("Setting Radio Power to Off");
+ mCommandsInterface.setRadioPower(false, null);
+ }
+ }
+
+ deleteAndCreatePhone(newVoiceRadioTech);
+
+ if (mResetModemOnRadioTechnologyChange && oldPowerState) { // restore power state
+ logd("Resetting Radio");
+ mCommandsInterface.setRadioPower(oldPowerState, null);
+ }
+
+ // Set the new interfaces in the proxy's
+ mIccSmsInterfaceManagerProxy.setmIccSmsInterfaceManager(
+ mActivePhone.getIccSmsInterfaceManager());
+ mIccPhoneBookInterfaceManagerProxy.setmIccPhoneBookInterfaceManager(mActivePhone
+ .getIccPhoneBookInterfaceManager());
+ mPhoneSubInfoProxy.setmPhoneSubInfo(this.mActivePhone.getPhoneSubInfo());
+
+ mCommandsInterface = ((PhoneBase)mActivePhone).mCM;
+
+ // Send an Intent to the PhoneApp that we had a radio technology change
+ Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(PhoneConstants.PHONE_NAME_KEY, mActivePhone.getPhoneName());
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+ }
+
+ private void deleteAndCreatePhone(int newVoiceRadioTech) {
+
+ String outgoingPhoneName = "Unknown";
+ Phone oldPhone = mActivePhone;
+
+ if (oldPhone != null) {
+ outgoingPhoneName = ((PhoneBase) oldPhone).getPhoneName();
+ }
+
+ logd("Switching Voice Phone : " + outgoingPhoneName + " >>> "
+ + (ServiceState.isGsm(newVoiceRadioTech) ? "GSM" : "CDMA"));
+
+ if (oldPhone != null) {
+ CallManager.getInstance().unregisterPhone(oldPhone);
+ logd("Disposing old phone..");
+ oldPhone.dispose();
+ }
+
+ // Give the garbage collector a hint to start the garbage collection
+ // asap NOTE this has been disabled since radio technology change could
+ // happen during e.g. a multimedia playing and could slow the system.
+ // Tests needs to be done to see the effects of the GC call here when
+ // system is busy.
+ // System.gc();
+
+ if (ServiceState.isCdma(newVoiceRadioTech)) {
+ mActivePhone = PhoneFactory.getCdmaPhone();
+ } else if (ServiceState.isGsm(newVoiceRadioTech)) {
+ mActivePhone = PhoneFactory.getGsmPhone();
+ }
+
+ if (oldPhone != null) {
+ oldPhone.removeReferences();
+ }
+
+ if(mActivePhone != null) {
+ CallManager.getInstance().registerPhone(mActivePhone);
+ }
+
+ oldPhone = null;
+ }
+
+ public ServiceState getServiceState() {
+ return mActivePhone.getServiceState();
+ }
+
+ public CellLocation getCellLocation() {
+ return mActivePhone.getCellLocation();
+ }
+
+ public PhoneConstants.DataState getDataConnectionState() {
+ return mActivePhone.getDataConnectionState(PhoneConstants.APN_TYPE_DEFAULT);
+ }
+
+ public PhoneConstants.DataState getDataConnectionState(String apnType) {
+ return mActivePhone.getDataConnectionState(apnType);
+ }
+
+ public DataActivityState getDataActivityState() {
+ return mActivePhone.getDataActivityState();
+ }
+
+ public Context getContext() {
+ return mActivePhone.getContext();
+ }
+
+ public void disableDnsCheck(boolean b) {
+ mActivePhone.disableDnsCheck(b);
+ }
+
+ public boolean isDnsCheckDisabled() {
+ return mActivePhone.isDnsCheckDisabled();
+ }
+
+ public PhoneConstants.State getState() {
+ return mActivePhone.getState();
+ }
+
+ public String getPhoneName() {
+ return mActivePhone.getPhoneName();
+ }
+
+ public int getPhoneType() {
+ return mActivePhone.getPhoneType();
+ }
+
+ public String[] getActiveApnTypes() {
+ return mActivePhone.getActiveApnTypes();
+ }
+
+ public String getActiveApnHost(String apnType) {
+ return mActivePhone.getActiveApnHost(apnType);
+ }
+
+ public LinkProperties getLinkProperties(String apnType) {
+ return mActivePhone.getLinkProperties(apnType);
+ }
+
+ public LinkCapabilities getLinkCapabilities(String apnType) {
+ return mActivePhone.getLinkCapabilities(apnType);
+ }
+
+ public SignalStrength getSignalStrength() {
+ return mActivePhone.getSignalStrength();
+ }
+
+ public void registerForUnknownConnection(Handler h, int what, Object obj) {
+ mActivePhone.registerForUnknownConnection(h, what, obj);
+ }
+
+ public void unregisterForUnknownConnection(Handler h) {
+ mActivePhone.unregisterForUnknownConnection(h);
+ }
+
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
+ mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
+ }
+
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mActivePhone.unregisterForPreciseCallStateChanged(h);
+ }
+
+ public void registerForNewRingingConnection(Handler h, int what, Object obj) {
+ mActivePhone.registerForNewRingingConnection(h, what, obj);
+ }
+
+ public void unregisterForNewRingingConnection(Handler h) {
+ mActivePhone.unregisterForNewRingingConnection(h);
+ }
+
+ public void registerForIncomingRing(Handler h, int what, Object obj) {
+ mActivePhone.registerForIncomingRing(h, what, obj);
+ }
+
+ public void unregisterForIncomingRing(Handler h) {
+ mActivePhone.unregisterForIncomingRing(h);
+ }
+
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ mActivePhone.registerForDisconnect(h, what, obj);
+ }
+
+ public void unregisterForDisconnect(Handler h) {
+ mActivePhone.unregisterForDisconnect(h);
+ }
+
+ public void registerForMmiInitiate(Handler h, int what, Object obj) {
+ mActivePhone.registerForMmiInitiate(h, what, obj);
+ }
+
+ public void unregisterForMmiInitiate(Handler h) {
+ mActivePhone.unregisterForMmiInitiate(h);
+ }
+
+ public void registerForMmiComplete(Handler h, int what, Object obj) {
+ mActivePhone.registerForMmiComplete(h, what, obj);
+ }
+
+ public void unregisterForMmiComplete(Handler h) {
+ mActivePhone.unregisterForMmiComplete(h);
+ }
+
+ public List extends MmiCode> getPendingMmiCodes() {
+ return mActivePhone.getPendingMmiCodes();
+ }
+
+ public void sendUssdResponse(String ussdMessge) {
+ mActivePhone.sendUssdResponse(ussdMessge);
+ }
+
+ public void registerForServiceStateChanged(Handler h, int what, Object obj) {
+ mActivePhone.registerForServiceStateChanged(h, what, obj);
+ }
+
+ public void unregisterForServiceStateChanged(Handler h) {
+ mActivePhone.unregisterForServiceStateChanged(h);
+ }
+
+ public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
+ mActivePhone.registerForSuppServiceNotification(h, what, obj);
+ }
+
+ public void unregisterForSuppServiceNotification(Handler h) {
+ mActivePhone.unregisterForSuppServiceNotification(h);
+ }
+
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
+ mActivePhone.registerForSuppServiceFailed(h, what, obj);
+ }
+
+ public void unregisterForSuppServiceFailed(Handler h) {
+ mActivePhone.unregisterForSuppServiceFailed(h);
+ }
+
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
+ mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj);
+ }
+
+ public void unregisterForInCallVoicePrivacyOn(Handler h){
+ mActivePhone.unregisterForInCallVoicePrivacyOn(h);
+ }
+
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
+ mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj);
+ }
+
+ public void unregisterForInCallVoicePrivacyOff(Handler h){
+ mActivePhone.unregisterForInCallVoicePrivacyOff(h);
+ }
+
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
+ mActivePhone.registerForCdmaOtaStatusChange(h,what,obj);
+ }
+
+ public void unregisterForCdmaOtaStatusChange(Handler h) {
+ mActivePhone.unregisterForCdmaOtaStatusChange(h);
+ }
+
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ mActivePhone.registerForSubscriptionInfoReady(h, what, obj);
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ mActivePhone.unregisterForSubscriptionInfoReady(h);
+ }
+
+ public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+ mActivePhone.registerForEcmTimerReset(h,what,obj);
+ }
+
+ public void unregisterForEcmTimerReset(Handler h) {
+ mActivePhone.unregisterForEcmTimerReset(h);
+ }
+
+ public void registerForRingbackTone(Handler h, int what, Object obj) {
+ mActivePhone.registerForRingbackTone(h,what,obj);
+ }
+
+ public void unregisterForRingbackTone(Handler h) {
+ mActivePhone.unregisterForRingbackTone(h);
+ }
+
+ public void registerForResendIncallMute(Handler h, int what, Object obj) {
+ mActivePhone.registerForResendIncallMute(h,what,obj);
+ }
+
+ public void unregisterForResendIncallMute(Handler h) {
+ mActivePhone.unregisterForResendIncallMute(h);
+ }
+
+ public boolean getIccRecordsLoaded() {
+ return mActivePhone.getIccRecordsLoaded();
+ }
+
+ public IccCard getIccCard() {
+ return mActivePhone.getIccCard();
+ }
+
+ public void acceptCall() throws CallStateException {
+ mActivePhone.acceptCall();
+ }
+
+ public void rejectCall() throws CallStateException {
+ mActivePhone.rejectCall();
+ }
+
+ public void switchHoldingAndActive() throws CallStateException {
+ mActivePhone.switchHoldingAndActive();
+ }
+
+ public boolean canConference() {
+ return mActivePhone.canConference();
+ }
+
+ public void conference() throws CallStateException {
+ mActivePhone.conference();
+ }
+
+ public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
+ mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete);
+ }
+
+ public void getEnhancedVoicePrivacy(Message onComplete) {
+ mActivePhone.getEnhancedVoicePrivacy(onComplete);
+ }
+
+ public boolean canTransfer() {
+ return mActivePhone.canTransfer();
+ }
+
+ public void explicitCallTransfer() throws CallStateException {
+ mActivePhone.explicitCallTransfer();
+ }
+
+ public void clearDisconnected() {
+ mActivePhone.clearDisconnected();
+ }
+
+ public Call getForegroundCall() {
+ return mActivePhone.getForegroundCall();
+ }
+
+ public Call getBackgroundCall() {
+ return mActivePhone.getBackgroundCall();
+ }
+
+ public Call getRingingCall() {
+ return mActivePhone.getRingingCall();
+ }
+
+ public Connection dial(String dialString) throws CallStateException {
+ return mActivePhone.dial(dialString);
+ }
+
+ public Connection dial(String dialString, UUSInfo uusInfo) throws CallStateException {
+ return mActivePhone.dial(dialString, uusInfo);
+ }
+
+ public boolean handlePinMmi(String dialString) {
+ return mActivePhone.handlePinMmi(dialString);
+ }
+
+ public boolean handleInCallMmiCommands(String command) throws CallStateException {
+ return mActivePhone.handleInCallMmiCommands(command);
+ }
+
+ public void sendDtmf(char c) {
+ mActivePhone.sendDtmf(c);
+ }
+
+ public void startDtmf(char c) {
+ mActivePhone.startDtmf(c);
+ }
+
+ public void stopDtmf() {
+ mActivePhone.stopDtmf();
+ }
+
+ public void setRadioPower(boolean power) {
+ mActivePhone.setRadioPower(power);
+ }
+
+ public boolean getMessageWaitingIndicator() {
+ return mActivePhone.getMessageWaitingIndicator();
+ }
+
+ public boolean getCallForwardingIndicator() {
+ return mActivePhone.getCallForwardingIndicator();
+ }
+
+ public String getLine1Number() {
+ return mActivePhone.getLine1Number();
+ }
+
+ public String getCdmaMin() {
+ return mActivePhone.getCdmaMin();
+ }
+
+ public boolean isMinInfoReady() {
+ return mActivePhone.isMinInfoReady();
+ }
+
+ public String getCdmaPrlVersion() {
+ return mActivePhone.getCdmaPrlVersion();
+ }
+
+ public String getLine1AlphaTag() {
+ return mActivePhone.getLine1AlphaTag();
+ }
+
+ public void setLine1Number(String alphaTag, String number, Message onComplete) {
+ mActivePhone.setLine1Number(alphaTag, number, onComplete);
+ }
+
+ public String getVoiceMailNumber() {
+ return mActivePhone.getVoiceMailNumber();
+ }
+
+ /** @hide */
+ public int getVoiceMessageCount(){
+ return mActivePhone.getVoiceMessageCount();
+ }
+
+ public String getVoiceMailAlphaTag() {
+ return mActivePhone.getVoiceMailAlphaTag();
+ }
+
+ public void setVoiceMailNumber(String alphaTag,String voiceMailNumber,
+ Message onComplete) {
+ mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
+ }
+
+ public void getCallForwardingOption(int commandInterfaceCFReason,
+ Message onComplete) {
+ mActivePhone.getCallForwardingOption(commandInterfaceCFReason,
+ onComplete);
+ }
+
+ public void setCallForwardingOption(int commandInterfaceCFReason,
+ int commandInterfaceCFAction, String dialingNumber,
+ int timerSeconds, Message onComplete) {
+ mActivePhone.setCallForwardingOption(commandInterfaceCFReason,
+ commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete);
+ }
+
+ public void getOutgoingCallerIdDisplay(Message onComplete) {
+ mActivePhone.getOutgoingCallerIdDisplay(onComplete);
+ }
+
+ public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
+ Message onComplete) {
+ mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode,
+ onComplete);
+ }
+
+ public void getCallWaiting(Message onComplete) {
+ mActivePhone.getCallWaiting(onComplete);
+ }
+
+ public void setCallWaiting(boolean enable, Message onComplete) {
+ mActivePhone.setCallWaiting(enable, onComplete);
+ }
+
+ public void getAvailableNetworks(Message response) {
+ mActivePhone.getAvailableNetworks(response);
+ }
+
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ mActivePhone.setNetworkSelectionModeAutomatic(response);
+ }
+
+ public void selectNetworkManually(OperatorInfo network, Message response) {
+ mActivePhone.selectNetworkManually(network, response);
+ }
+
+ public void setPreferredNetworkType(int networkType, Message response) {
+ mActivePhone.setPreferredNetworkType(networkType, response);
+ }
+
+ public void getPreferredNetworkType(Message response) {
+ mActivePhone.getPreferredNetworkType(response);
+ }
+
+ public void getNeighboringCids(Message response) {
+ mActivePhone.getNeighboringCids(response);
+ }
+
+ public void setOnPostDialCharacter(Handler h, int what, Object obj) {
+ mActivePhone.setOnPostDialCharacter(h, what, obj);
+ }
+
+ public void setMute(boolean muted) {
+ mActivePhone.setMute(muted);
+ }
+
+ public boolean getMute() {
+ return mActivePhone.getMute();
+ }
+
+ public void setEchoSuppressionEnabled(boolean enabled) {
+ mActivePhone.setEchoSuppressionEnabled(enabled);
+ }
+
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ mActivePhone.invokeOemRilRequestRaw(data, response);
+ }
+
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ mActivePhone.invokeOemRilRequestStrings(strings, response);
+ }
+
+ public void getDataCallList(Message response) {
+ mActivePhone.getDataCallList(response);
+ }
+
+ public void updateServiceLocation() {
+ mActivePhone.updateServiceLocation();
+ }
+
+ public void enableLocationUpdates() {
+ mActivePhone.enableLocationUpdates();
+ }
+
+ public void disableLocationUpdates() {
+ mActivePhone.disableLocationUpdates();
+ }
+
+ public void setUnitTestMode(boolean f) {
+ mActivePhone.setUnitTestMode(f);
+ }
+
+ public boolean getUnitTestMode() {
+ return mActivePhone.getUnitTestMode();
+ }
+
+ public void setBandMode(int bandMode, Message response) {
+ mActivePhone.setBandMode(bandMode, response);
+ }
+
+ public void queryAvailableBandMode(Message response) {
+ mActivePhone.queryAvailableBandMode(response);
+ }
+
+ public boolean getDataRoamingEnabled() {
+ return mActivePhone.getDataRoamingEnabled();
+ }
+
+ public void setDataRoamingEnabled(boolean enable) {
+ mActivePhone.setDataRoamingEnabled(enable);
+ }
+
+ public void queryCdmaRoamingPreference(Message response) {
+ mActivePhone.queryCdmaRoamingPreference(response);
+ }
+
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response);
+ }
+
+ public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
+ mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response);
+ }
+
+ public SimulatedRadioControl getSimulatedRadioControl() {
+ return mActivePhone.getSimulatedRadioControl();
+ }
+
+ public int enableApnType(String type) {
+ return mActivePhone.enableApnType(type);
+ }
+
+ public int disableApnType(String type) {
+ return mActivePhone.disableApnType(type);
+ }
+
+ public boolean isDataConnectivityPossible() {
+ return mActivePhone.isDataConnectivityPossible(PhoneConstants.APN_TYPE_DEFAULT);
+ }
+
+ public boolean isDataConnectivityPossible(String apnType) {
+ return mActivePhone.isDataConnectivityPossible(apnType);
+ }
+
+ public String getDeviceId() {
+ return mActivePhone.getDeviceId();
+ }
+
+ public String getDeviceSvn() {
+ return mActivePhone.getDeviceSvn();
+ }
+
+ public String getSubscriberId() {
+ return mActivePhone.getSubscriberId();
+ }
+
+ public String getIccSerialNumber() {
+ return mActivePhone.getIccSerialNumber();
+ }
+
+ public String getEsn() {
+ return mActivePhone.getEsn();
+ }
+
+ public String getMeid() {
+ return mActivePhone.getMeid();
+ }
+
+ public String getMsisdn() {
+ return mActivePhone.getMsisdn();
+ }
+
+ public String getImei() {
+ return mActivePhone.getImei();
+ }
+
+ public PhoneSubInfo getPhoneSubInfo(){
+ return mActivePhone.getPhoneSubInfo();
+ }
+
+ public IccSmsInterfaceManager getIccSmsInterfaceManager(){
+ return mActivePhone.getIccSmsInterfaceManager();
+ }
+
+ public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
+ return mActivePhone.getIccPhoneBookInterfaceManager();
+ }
+
+ public void setTTYMode(int ttyMode, Message onComplete) {
+ mActivePhone.setTTYMode(ttyMode, onComplete);
+ }
+
+ public void queryTTYMode(Message onComplete) {
+ mActivePhone.queryTTYMode(onComplete);
+ }
+
+ public void activateCellBroadcastSms(int activate, Message response) {
+ mActivePhone.activateCellBroadcastSms(activate, response);
+ }
+
+ public void getCellBroadcastSmsConfig(Message response) {
+ mActivePhone.getCellBroadcastSmsConfig(response);
+ }
+
+ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
+ mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response);
+ }
+
+ public void notifyDataActivity() {
+ mActivePhone.notifyDataActivity();
+ }
+
+ public void getSmscAddress(Message result) {
+ mActivePhone.getSmscAddress(result);
+ }
+
+ public void setSmscAddress(String address, Message result) {
+ mActivePhone.setSmscAddress(address, result);
+ }
+
+ public int getCdmaEriIconIndex() {
+ return mActivePhone.getCdmaEriIconIndex();
+ }
+
+ public String getCdmaEriText() {
+ return mActivePhone.getCdmaEriText();
+ }
+
+ public int getCdmaEriIconMode() {
+ return mActivePhone.getCdmaEriIconMode();
+ }
+
+ public Phone getActivePhone() {
+ return mActivePhone;
+ }
+
+ public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
+ mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete);
+ }
+
+ public void exitEmergencyCallbackMode(){
+ mActivePhone.exitEmergencyCallbackMode();
+ }
+
+ public boolean needsOtaServiceProvisioning(){
+ return mActivePhone.needsOtaServiceProvisioning();
+ }
+
+ public boolean isOtaSpNumber(String dialStr){
+ return mActivePhone.isOtaSpNumber(dialStr);
+ }
+
+ public void registerForCallWaiting(Handler h, int what, Object obj){
+ mActivePhone.registerForCallWaiting(h,what,obj);
+ }
+
+ public void unregisterForCallWaiting(Handler h){
+ mActivePhone.unregisterForCallWaiting(h);
+ }
+
+ public void registerForSignalInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForSignalInfo(h,what,obj);
+ }
+
+ public void unregisterForSignalInfo(Handler h) {
+ mActivePhone.unregisterForSignalInfo(h);
+ }
+
+ public void registerForDisplayInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForDisplayInfo(h,what,obj);
+ }
+
+ public void unregisterForDisplayInfo(Handler h) {
+ mActivePhone.unregisterForDisplayInfo(h);
+ }
+
+ public void registerForNumberInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForNumberInfo(h, what, obj);
+ }
+
+ public void unregisterForNumberInfo(Handler h) {
+ mActivePhone.unregisterForNumberInfo(h);
+ }
+
+ public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForRedirectedNumberInfo(h, what, obj);
+ }
+
+ public void unregisterForRedirectedNumberInfo(Handler h) {
+ mActivePhone.unregisterForRedirectedNumberInfo(h);
+ }
+
+ public void registerForLineControlInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForLineControlInfo( h, what, obj);
+ }
+
+ public void unregisterForLineControlInfo(Handler h) {
+ mActivePhone.unregisterForLineControlInfo(h);
+ }
+
+ public void registerFoT53ClirlInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerFoT53ClirlInfo(h, what, obj);
+ }
+
+ public void unregisterForT53ClirInfo(Handler h) {
+ mActivePhone.unregisterForT53ClirInfo(h);
+ }
+
+ public void registerForT53AudioControlInfo(Handler h, int what, Object obj) {
+ mActivePhone.registerForT53AudioControlInfo( h, what, obj);
+ }
+
+ public void unregisterForT53AudioControlInfo(Handler h) {
+ mActivePhone.unregisterForT53AudioControlInfo(h);
+ }
+
+ public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
+ mActivePhone.setOnEcbModeExitResponse(h,what,obj);
+ }
+
+ public void unsetOnEcbModeExitResponse(Handler h){
+ mActivePhone.unsetOnEcbModeExitResponse(h);
+ }
+
+ public boolean isCspPlmnEnabled() {
+ return mActivePhone.isCspPlmnEnabled();
+ }
+
+ public IsimRecords getIsimRecords() {
+ return mActivePhone.getIsimRecords();
+ }
+
+ public void requestIsimAuthentication(String nonce, Message response) {
+ mActivePhone.requestIsimAuthentication(nonce, response);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getLteOnCdmaMode() {
+ return mActivePhone.getLteOnCdmaMode();
+ }
+
+ @Override
+ public void setVoiceMessageWaiting(int line, int countWaiting) {
+ mActivePhone.setVoiceMessageWaiting(line, countWaiting);
+ }
+
+ @Override
+ public UsimServiceTable getUsimServiceTable() {
+ return mActivePhone.getUsimServiceTable();
+ }
+
+ public void dispose() {
+ mCommandsInterface.unregisterForOn(this);
+ mCommandsInterface.unregisterForVoiceRadioTechChanged(this);
+ mCommandsInterface.unregisterForRilConnected(this);
+ }
+
+ public void removeReferences() {
+ mActivePhone = null;
+ mCommandsInterface = null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/PhoneStateIntentReceiver.java b/src/java/com/android/internal/telephony/PhoneStateIntentReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..89084ac39f5c96ff74bb445d98b2da612d430da2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneStateIntentReceiver.java
@@ -0,0 +1,203 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+/**
+ *
+ * DO NOT USE THIS CLASS:
+ *
+ * Use android.telephony.TelephonyManager and PhoneStateListener instead.
+ *
+ *
+ */
+@Deprecated
+public final class PhoneStateIntentReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "PHONE";
+ private static final boolean DBG = false;
+
+ private static final int NOTIF_PHONE = 1 << 0;
+ private static final int NOTIF_SERVICE = 1 << 1;
+ private static final int NOTIF_SIGNAL = 1 << 2;
+
+ private static final int NOTIF_MAX = 1 << 5;
+
+ PhoneConstants.State mPhoneState = PhoneConstants.State.IDLE;
+ ServiceState mServiceState = new ServiceState();
+ SignalStrength mSignalStrength = new SignalStrength();
+
+ private Context mContext;
+ private Handler mTarget;
+ private IntentFilter mFilter;
+ private int mWants;
+ private int mPhoneStateEventWhat;
+ private int mServiceStateEventWhat;
+ private int mLocationEventWhat;
+ private int mAsuEventWhat;
+
+ public PhoneStateIntentReceiver() {
+ super();
+ mFilter = new IntentFilter();
+ }
+
+ public PhoneStateIntentReceiver(Context context, Handler target) {
+ this();
+ setContext(context);
+ setTarget(target);
+ }
+
+ public void setContext(Context c) {
+ mContext = c;
+ }
+
+ public void setTarget(Handler h) {
+ mTarget = h;
+ }
+
+ public PhoneConstants.State getPhoneState() {
+ if ((mWants & NOTIF_PHONE) == 0) {
+ throw new RuntimeException
+ ("client must call notifyPhoneCallState(int)");
+ }
+ return mPhoneState;
+ }
+
+ public ServiceState getServiceState() {
+ if ((mWants & NOTIF_SERVICE) == 0) {
+ throw new RuntimeException
+ ("client must call notifyServiceState(int)");
+ }
+ return mServiceState;
+ }
+
+ /**
+ * Returns current signal strength in as an asu 0..31
+ *
+ * Throws RuntimeException if client has not called notifySignalStrength()
+ */
+ public int getSignalStrengthLevelAsu() {
+ // TODO: use new SignalStrength instead of asu
+ if ((mWants & NOTIF_SIGNAL) == 0) {
+ throw new RuntimeException
+ ("client must call notifySignalStrength(int)");
+ }
+ return mSignalStrength.getAsuLevel();
+ }
+
+ /**
+ * Return current signal strength in "dBm", ranging from -113 - -51dBm
+ * or -1 if unknown
+ *
+ * @return signal strength in dBm, -1 if not yet updated
+ * Throws RuntimeException if client has not called notifySignalStrength()
+ */
+ public int getSignalStrengthDbm() {
+ if ((mWants & NOTIF_SIGNAL) == 0) {
+ throw new RuntimeException
+ ("client must call notifySignalStrength(int)");
+ }
+ return mSignalStrength.getDbm();
+ }
+
+ public void notifyPhoneCallState(int eventWhat) {
+ mWants |= NOTIF_PHONE;
+ mPhoneStateEventWhat = eventWhat;
+ mFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ }
+
+ public boolean getNotifyPhoneCallState() {
+ return ((mWants & NOTIF_PHONE) != 0);
+ }
+
+ public void notifyServiceState(int eventWhat) {
+ mWants |= NOTIF_SERVICE;
+ mServiceStateEventWhat = eventWhat;
+ mFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ }
+
+ public boolean getNotifyServiceState() {
+ return ((mWants & NOTIF_SERVICE) != 0);
+ }
+
+ public void notifySignalStrength (int eventWhat) {
+ mWants |= NOTIF_SIGNAL;
+ mAsuEventWhat = eventWhat;
+ mFilter.addAction(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
+ }
+
+ public boolean getNotifySignalStrength() {
+ return ((mWants & NOTIF_SIGNAL) != 0);
+ }
+
+ public void registerIntent() {
+ mContext.registerReceiver(this, mFilter);
+ }
+
+ public void unregisterIntent() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ try {
+ if (TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED.equals(action)) {
+ mSignalStrength = SignalStrength.newFromBundle(intent.getExtras());
+
+ if (mTarget != null && getNotifySignalStrength()) {
+ Message message = Message.obtain(mTarget, mAsuEventWhat);
+ mTarget.sendMessage(message);
+ }
+ } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
+ if (DBG) Log.d(LOG_TAG, "onReceiveIntent: ACTION_PHONE_STATE_CHANGED, state="
+ + intent.getStringExtra(PhoneConstants.STATE_KEY));
+ String phoneState = intent.getStringExtra(PhoneConstants.STATE_KEY);
+ mPhoneState = (PhoneConstants.State) Enum.valueOf(
+ PhoneConstants.State.class, phoneState);
+
+ if (mTarget != null && getNotifyPhoneCallState()) {
+ Message message = Message.obtain(mTarget,
+ mPhoneStateEventWhat);
+ mTarget.sendMessage(message);
+ }
+ } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ mServiceState = ServiceState.newFromBundle(intent.getExtras());
+
+ if (mTarget != null && getNotifyServiceState()) {
+ Message message = Message.obtain(mTarget,
+ mServiceStateEventWhat);
+ mTarget.sendMessage(message);
+ }
+ }
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "[PhoneStateIntentRecv] caught " + ex);
+ ex.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfo.java b/src/java/com/android/internal/telephony/PhoneSubInfo.java
new file mode 100755
index 0000000000000000000000000000000000000000..e8449cefe16c1ce0d8836230c4baf5bce83e67cd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.ims.IsimRecords;
+
+public class PhoneSubInfo extends IPhoneSubInfo.Stub {
+ static final String LOG_TAG = "PHONE";
+ private Phone mPhone;
+ private Context mContext;
+ private static final String READ_PHONE_STATE =
+ android.Manifest.permission.READ_PHONE_STATE;
+ // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
+ private static final String CALL_PRIVILEGED =
+ android.Manifest.permission.CALL_PRIVILEGED;
+ private static final String READ_PRIVILEGED_PHONE_STATE =
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
+ public PhoneSubInfo(Phone phone) {
+ mPhone = phone;
+ mContext = phone.getContext();
+ }
+
+ public void dispose() {
+ }
+
+ protected void finalize() {
+ try {
+ super.finalize();
+ } catch (Throwable throwable) {
+ Log.e(LOG_TAG, "Error while finalizing:", throwable);
+ }
+ Log.d(LOG_TAG, "PhoneSubInfo finalized");
+ }
+
+ /**
+ * 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");
+ return mPhone.getDeviceId();
+ }
+
+ /**
+ * 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");
+ 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");
+ return mPhone.getSubscriberId();
+ }
+
+ /**
+ * Retrieves the serial number of the ICC, if applicable.
+ */
+ public String getIccSerialNumber() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ return mPhone.getIccSerialNumber();
+ }
+
+ /**
+ * Retrieves the phone number string for line 1.
+ */
+ public String getLine1Number() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ return mPhone.getLine1Number();
+ }
+
+ /**
+ * Retrieves the alpha identifier for line 1.
+ */
+ public String getLine1AlphaTag() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ return (String) mPhone.getLine1AlphaTag();
+ }
+
+ /**
+ * Retrieves the MSISDN string.
+ */
+ public String getMsisdn() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ return mPhone.getMsisdn();
+ }
+
+ /**
+ * Retrieves the voice mail number.
+ */
+ public String getVoiceMailNumber() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number);
+ return number;
+ }
+
+ /**
+ * Retrieves the complete voice mail number.
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
+ "Requires CALL_PRIVILEGED");
+ String number = mPhone.getVoiceMailNumber();
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number);
+ return number;
+ }
+
+ /**
+ * Retrieves the alpha identifier associated with the voice mail number.
+ */
+ public String getVoiceMailAlphaTag() {
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+ return (String) mPhone.getVoiceMailAlphaTag();
+ }
+
+ /**
+ * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
+ * @return the IMPI, or null if not present or not loaded
+ */
+ public String getIsimImpi() {
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimImpi();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS home network domain name that was loaded from the ISIM.
+ * @return the IMS domain name, or null if not present or not loaded
+ */
+ public String getIsimDomain() {
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimDomain();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
+ * @return an array of IMPU strings, with one IMPU per string, or null if
+ * not present or not loaded
+ */
+ public String[] getIsimImpu() {
+ mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+ "Requires READ_PRIVILEGED_PHONE_STATE");
+ IsimRecords isim = mPhone.getIsimRecords();
+ if (isim != null) {
+ return isim.getIsimImpu();
+ } else {
+ return null;
+ }
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump PhoneSubInfo from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("Phone Subscriber Info:");
+ pw.println(" Phone Type = " + mPhone.getPhoneName());
+ pw.println(" Device ID = " + mPhone.getDeviceId());
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
new file mode 100755
index 0000000000000000000000000000000000000000..bd130deb00d4851184fd79bbc0571cce89083e2d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -0,0 +1,133 @@
+/*
+ * 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 java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.ServiceManager;
+
+
+public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub {
+ private PhoneSubInfo mPhoneSubInfo;
+
+ public PhoneSubInfoProxy(PhoneSubInfo phoneSubInfo) {
+ mPhoneSubInfo = phoneSubInfo;
+ if(ServiceManager.getService("iphonesubinfo") == null) {
+ ServiceManager.addService("iphonesubinfo", this);
+ }
+ }
+
+ public void setmPhoneSubInfo(PhoneSubInfo phoneSubInfo) {
+ this.mPhoneSubInfo = phoneSubInfo;
+ }
+
+ public String getDeviceId() {
+ return mPhoneSubInfo.getDeviceId();
+ }
+
+ public String getDeviceSvn() {
+ return mPhoneSubInfo.getDeviceSvn();
+ }
+
+ /**
+ * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
+ */
+ public String getSubscriberId() {
+ return mPhoneSubInfo.getSubscriberId();
+ }
+
+ /**
+ * Retrieves the serial number of the ICC, if applicable.
+ */
+ public String getIccSerialNumber() {
+ return mPhoneSubInfo.getIccSerialNumber();
+ }
+
+ /**
+ * Retrieves the phone number string for line 1.
+ */
+ public String getLine1Number() {
+ return mPhoneSubInfo.getLine1Number();
+ }
+
+ /**
+ * Retrieves the alpha identifier for line 1.
+ */
+ public String getLine1AlphaTag() {
+ return mPhoneSubInfo.getLine1AlphaTag();
+ }
+
+ /**
+ * Retrieves the MSISDN Number.
+ */
+ public String getMsisdn() {
+ return mPhoneSubInfo.getMsisdn();
+ }
+
+ /**
+ * Retrieves the voice mail number.
+ */
+ public String getVoiceMailNumber() {
+ return mPhoneSubInfo.getVoiceMailNumber();
+ }
+
+ /**
+ * Retrieves the complete voice mail number.
+ */
+ public String getCompleteVoiceMailNumber() {
+ return mPhoneSubInfo.getCompleteVoiceMailNumber();
+ }
+
+ /**
+ * Retrieves the alpha identifier associated with the voice mail number.
+ */
+ public String getVoiceMailAlphaTag() {
+ return mPhoneSubInfo.getVoiceMailAlphaTag();
+ }
+
+ /**
+ * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
+ * @return the IMPI, or null if not present or not loaded
+ */
+ public String getIsimImpi() {
+ return mPhoneSubInfo.getIsimImpi();
+ }
+
+ /**
+ * Returns the IMS home network domain name that was loaded from the ISIM.
+ * @return the IMS domain name, or null if not present or not loaded
+ */
+ public String getIsimDomain() {
+ return mPhoneSubInfo.getIsimDomain();
+ }
+
+ /**
+ * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
+ * @return an array of IMPU strings, with one IMPU per string, or null if
+ * not present or not loaded
+ */
+ public String[] getIsimImpu() {
+ return mPhoneSubInfo.getIsimImpu();
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mPhoneSubInfo.dump(fd, pw, args);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
new file mode 100644
index 0000000000000000000000000000000000000000..b14f6c8ea60fd2693f52eb2c63d35242d73461ab
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -0,0 +1,3861 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.RILConstants.*;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_GPRS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSDPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSUPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSPA;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.PowerManager.WakeLock;
+import android.telephony.NeighboringCellInfo;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+import com.android.internal.telephony.cdma.CdmaInformationRecords;
+import com.android.internal.telephony.IccRefreshResponse;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * {@hide}
+ */
+class RILRequest {
+ static final String LOG_TAG = "RILJ";
+
+ //***** Class Variables
+ static int sNextSerial = 0;
+ static Object sSerialMonitor = new Object();
+ private static Object sPoolSync = new Object();
+ private static RILRequest sPool = null;
+ private static int sPoolSize = 0;
+ private static final int MAX_POOL_SIZE = 4;
+
+ //***** Instance Variables
+ int mSerial;
+ int mRequest;
+ Message mResult;
+ Parcel mp;
+ RILRequest mNext;
+
+ /**
+ * Retrieves a new RILRequest instance from the pool.
+ *
+ * @param request RIL_REQUEST_*
+ * @param result sent when operation completes
+ * @return a RILRequest instance from the pool.
+ */
+ static RILRequest obtain(int request, Message result) {
+ RILRequest rr = null;
+
+ synchronized(sPoolSync) {
+ if (sPool != null) {
+ rr = sPool;
+ sPool = rr.mNext;
+ rr.mNext = null;
+ sPoolSize--;
+ }
+ }
+
+ if (rr == null) {
+ rr = new RILRequest();
+ }
+
+ synchronized(sSerialMonitor) {
+ rr.mSerial = sNextSerial++;
+ }
+ rr.mRequest = request;
+ rr.mResult = result;
+ rr.mp = Parcel.obtain();
+
+ if (result != null && result.getTarget() == null) {
+ throw new NullPointerException("Message target must not be null");
+ }
+
+ // first elements in any RIL Parcel
+ rr.mp.writeInt(request);
+ rr.mp.writeInt(rr.mSerial);
+
+ return rr;
+ }
+
+ /**
+ * Returns a RILRequest instance to the pool.
+ *
+ * Note: This should only be called once per use.
+ */
+ void release() {
+ synchronized (sPoolSync) {
+ if (sPoolSize < MAX_POOL_SIZE) {
+ this.mNext = sPool;
+ sPool = this;
+ sPoolSize++;
+ mResult = null;
+ }
+ }
+ }
+
+ private RILRequest() {
+ }
+
+ static void
+ resetSerial() {
+ synchronized(sSerialMonitor) {
+ sNextSerial = 0;
+ }
+ }
+
+ String
+ serialString() {
+ //Cheesy way to do %04d
+ StringBuilder sb = new StringBuilder(8);
+ String sn;
+
+ sn = Integer.toString(mSerial);
+
+ //sb.append("J[");
+ sb.append('[');
+ for (int i = 0, s = sn.length() ; i < 4 - s; i++) {
+ sb.append('0');
+ }
+
+ sb.append(sn);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ void
+ onError(int error, Object ret) {
+ CommandException ex;
+
+ ex = CommandException.fromRilErrno(error);
+
+ if (RIL.RILJ_LOGD) Log.d(LOG_TAG, serialString() + "< "
+ + RIL.requestToString(mRequest)
+ + " error: " + ex);
+
+ if (mResult != null) {
+ AsyncResult.forMessage(mResult, ret, ex);
+ mResult.sendToTarget();
+ }
+
+ if (mp != null) {
+ mp.recycle();
+ mp = null;
+ }
+ }
+}
+
+
+/**
+ * RIL implementation of the CommandsInterface.
+ * FIXME public only for testing
+ *
+ * {@hide}
+ */
+public final class RIL extends BaseCommands implements CommandsInterface {
+ static final String LOG_TAG = "RILJ";
+ static final boolean RILJ_LOGD = true;
+ static final boolean RILJ_LOGV = false; // STOP SHIP if true
+
+ /**
+ * Wake lock timeout should be longer than the longest timeout in
+ * the vendor ril.
+ */
+ private static final int DEFAULT_WAKE_LOCK_TIMEOUT = 60000;
+
+ //***** Instance Variables
+
+ LocalSocket mSocket;
+ HandlerThread mSenderThread;
+ RILSender mSender;
+ Thread mReceiverThread;
+ RILReceiver mReceiver;
+ WakeLock mWakeLock;
+ int mWakeLockTimeout;
+ // The number of requests pending to be sent out, it increases before calling
+ // EVENT_SEND and decreases while handling EVENT_SEND. It gets cleared while
+ // WAKE_LOCK_TIMEOUT occurs.
+ int mRequestMessagesPending;
+ // The number of requests sent out but waiting for response. It increases while
+ // sending request and decreases while handling response. It should match
+ // mRequestList.size() unless there are requests no replied while
+ // WAKE_LOCK_TIMEOUT occurs.
+ int mRequestMessagesWaiting;
+
+ //I'd rather this be LinkedList or something
+ ArrayList mRequestsList = new ArrayList();
+
+ Object mLastNITZTimeInfo;
+
+ // When we are testing emergency calls
+ AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
+
+ //***** Events
+
+ static final int EVENT_SEND = 1;
+ static final int EVENT_WAKE_LOCK_TIMEOUT = 2;
+
+ //***** Constants
+
+ // match with constant in ril.cpp
+ static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
+ static final int RESPONSE_SOLICITED = 0;
+ static final int RESPONSE_UNSOLICITED = 1;
+
+ static final String SOCKET_NAME_RIL = "rild";
+
+ static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
+
+ // The number of the required config values for broadcast SMS stored in the C struct
+ // RIL_CDMA_BroadcastServiceInfo
+ private static final int CDMA_BSI_NO_OF_INTS_STRUCT = 3;
+
+ private static final int CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES = 31;
+
+ BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ sendScreenState(true);
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ sendScreenState(false);
+ } else {
+ Log.w(LOG_TAG, "RIL received unexpected Intent: " + intent.getAction());
+ }
+ }
+ };
+
+ class RILSender extends Handler implements Runnable {
+ public RILSender(Looper looper) {
+ super(looper);
+ }
+
+ // Only allocated once
+ byte[] dataLength = new byte[4];
+
+ //***** Runnable implementation
+ public void
+ run() {
+ //setup if needed
+ }
+
+
+ //***** Handler implementation
+ @Override public void
+ handleMessage(Message msg) {
+ RILRequest rr = (RILRequest)(msg.obj);
+ RILRequest req = null;
+
+ switch (msg.what) {
+ case EVENT_SEND:
+ /**
+ * mRequestMessagePending++ already happened for every
+ * EVENT_SEND, thus we must make sure
+ * mRequestMessagePending-- happens once and only once
+ */
+ boolean alreadySubtracted = false;
+ try {
+ LocalSocket s;
+
+ s = mSocket;
+
+ if (s == null) {
+ rr.onError(RADIO_NOT_AVAILABLE, null);
+ rr.release();
+ if (mRequestMessagesPending > 0)
+ mRequestMessagesPending--;
+ alreadySubtracted = true;
+ return;
+ }
+
+ synchronized (mRequestsList) {
+ mRequestsList.add(rr);
+ mRequestMessagesWaiting++;
+ }
+
+ if (mRequestMessagesPending > 0)
+ mRequestMessagesPending--;
+ alreadySubtracted = true;
+
+ byte[] data;
+
+ data = rr.mp.marshall();
+ rr.mp.recycle();
+ rr.mp = null;
+
+ if (data.length > RIL_MAX_COMMAND_BYTES) {
+ throw new RuntimeException(
+ "Parcel larger than max bytes allowed! "
+ + data.length);
+ }
+
+ // parcel length in big endian
+ dataLength[0] = dataLength[1] = 0;
+ dataLength[2] = (byte)((data.length >> 8) & 0xff);
+ dataLength[3] = (byte)((data.length) & 0xff);
+
+ //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes");
+
+ s.getOutputStream().write(dataLength);
+ s.getOutputStream().write(data);
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "IOException", ex);
+ req = findAndRemoveRequestFromList(rr.mSerial);
+ // make sure this request has not already been handled,
+ // eg, if RILReceiver cleared the list.
+ if (req != null || !alreadySubtracted) {
+ rr.onError(RADIO_NOT_AVAILABLE, null);
+ rr.release();
+ }
+ } catch (RuntimeException exc) {
+ Log.e(LOG_TAG, "Uncaught exception ", exc);
+ req = findAndRemoveRequestFromList(rr.mSerial);
+ // make sure this request has not already been handled,
+ // eg, if RILReceiver cleared the list.
+ if (req != null || !alreadySubtracted) {
+ rr.onError(GENERIC_FAILURE, null);
+ rr.release();
+ }
+ } finally {
+ // Note: We are "Done" only if there are no outstanding
+ // requests or replies. Thus this code path will only release
+ // the wake lock on errors.
+ releaseWakeLockIfDone();
+ }
+
+ if (!alreadySubtracted && mRequestMessagesPending > 0) {
+ mRequestMessagesPending--;
+ }
+
+ break;
+
+ case EVENT_WAKE_LOCK_TIMEOUT:
+ // Haven't heard back from the last request. Assume we're
+ // not getting a response and release the wake lock.
+ synchronized (mWakeLock) {
+ if (mWakeLock.isHeld()) {
+ // The timer of WAKE_LOCK_TIMEOUT is reset with each
+ // new send request. So when WAKE_LOCK_TIMEOUT occurs
+ // all requests in mRequestList already waited at
+ // least DEFAULT_WAKE_LOCK_TIMEOUT but no response.
+ // Reset mRequestMessagesWaiting to enable
+ // releaseWakeLockIfDone().
+ //
+ // Note: Keep mRequestList so that delayed response
+ // can still be handled when response finally comes.
+ if (mRequestMessagesWaiting != 0) {
+ Log.d(LOG_TAG, "NOTE: mReqWaiting is NOT 0 but"
+ + mRequestMessagesWaiting + " at TIMEOUT, reset!"
+ + " There still msg waitng for response");
+
+ mRequestMessagesWaiting = 0;
+
+ if (RILJ_LOGD) {
+ synchronized (mRequestsList) {
+ int count = mRequestsList.size();
+ Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " +
+ " mRequestList=" + count);
+
+ for (int i = 0; i < count; i++) {
+ rr = mRequestsList.get(i);
+ Log.d(LOG_TAG, i + ": [" + rr.mSerial + "] "
+ + requestToString(rr.mRequest));
+ }
+ }
+ }
+ }
+ // mRequestMessagesPending shows how many
+ // requests are waiting to be sent (and before
+ // to be added in request list) since star the
+ // WAKE_LOCK_TIMEOUT timer. Since WAKE_LOCK_TIMEOUT
+ // is the expected time to get response, all requests
+ // should already sent out (i.e.
+ // mRequestMessagesPending is 0 )while TIMEOUT occurs.
+ if (mRequestMessagesPending != 0) {
+ Log.e(LOG_TAG, "ERROR: mReqPending is NOT 0 but"
+ + mRequestMessagesPending + " at TIMEOUT, reset!");
+ mRequestMessagesPending = 0;
+
+ }
+ mWakeLock.release();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads in a single RIL message off the wire. A RIL message consists
+ * of a 4-byte little-endian length and a subsequent series of bytes.
+ * The final message (length header omitted) is read into
+ * buffer
and the length of the final message (less header)
+ * is returned. A return value of -1 indicates end-of-stream.
+ *
+ * @param is non-null; Stream to read from
+ * @param buffer Buffer to fill in. Must be as large as maximum
+ * message size, or an ArrayOutOfBounds exception will be thrown.
+ * @return Length of message less header, or -1 on end of stream.
+ * @throws IOException
+ */
+ private static int readRilMessage(InputStream is, byte[] buffer)
+ throws IOException {
+ int countRead;
+ int offset;
+ int remaining;
+ int messageLength;
+
+ // First, read in the length of the message
+ offset = 0;
+ remaining = 4;
+ do {
+ countRead = is.read(buffer, offset, remaining);
+
+ if (countRead < 0 ) {
+ Log.e(LOG_TAG, "Hit EOS reading message length");
+ return -1;
+ }
+
+ offset += countRead;
+ remaining -= countRead;
+ } while (remaining > 0);
+
+ messageLength = ((buffer[0] & 0xff) << 24)
+ | ((buffer[1] & 0xff) << 16)
+ | ((buffer[2] & 0xff) << 8)
+ | (buffer[3] & 0xff);
+
+ // Then, re-use the buffer and read in the message itself
+ offset = 0;
+ remaining = messageLength;
+ do {
+ countRead = is.read(buffer, offset, remaining);
+
+ if (countRead < 0 ) {
+ Log.e(LOG_TAG, "Hit EOS reading message. messageLength=" + messageLength
+ + " remaining=" + remaining);
+ return -1;
+ }
+
+ offset += countRead;
+ remaining -= countRead;
+ } while (remaining > 0);
+
+ return messageLength;
+ }
+
+ class RILReceiver implements Runnable {
+ byte[] buffer;
+
+ RILReceiver() {
+ buffer = new byte[RIL_MAX_COMMAND_BYTES];
+ }
+
+ public void
+ run() {
+ int retryCount = 0;
+
+ try {for (;;) {
+ LocalSocket s = null;
+ LocalSocketAddress l;
+
+ try {
+ s = new LocalSocket();
+ l = new LocalSocketAddress(SOCKET_NAME_RIL,
+ LocalSocketAddress.Namespace.RESERVED);
+ s.connect(l);
+ } catch (IOException ex){
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (IOException ex2) {
+ //ignore failure to close after failure to connect
+ }
+
+ // don't print an error message after the the first time
+ // or after the 8th time
+
+ if (retryCount == 8) {
+ Log.e (LOG_TAG,
+ "Couldn't find '" + SOCKET_NAME_RIL
+ + "' socket after " + retryCount
+ + " times, continuing to retry silently");
+ } else if (retryCount > 0 && retryCount < 8) {
+ Log.i (LOG_TAG,
+ "Couldn't find '" + SOCKET_NAME_RIL
+ + "' socket; retrying after timeout");
+ }
+
+ try {
+ Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
+ } catch (InterruptedException er) {
+ }
+
+ retryCount++;
+ continue;
+ }
+
+ retryCount = 0;
+
+ mSocket = s;
+ Log.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket");
+
+ int length = 0;
+ try {
+ InputStream is = mSocket.getInputStream();
+
+ for (;;) {
+ Parcel p;
+
+ length = readRilMessage(is, buffer);
+
+ if (length < 0) {
+ // End-of-stream reached
+ break;
+ }
+
+ p = Parcel.obtain();
+ p.unmarshall(buffer, 0, length);
+ p.setDataPosition(0);
+
+ //Log.v(LOG_TAG, "Read packet: " + length + " bytes");
+
+ processResponse(p);
+ p.recycle();
+ }
+ } catch (java.io.IOException ex) {
+ Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed",
+ ex);
+ } catch (Throwable tr) {
+ Log.e(LOG_TAG, "Uncaught exception read length=" + length +
+ "Exception:" + tr.toString());
+ }
+
+ Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL
+ + "' socket");
+
+ setRadioState (RadioState.RADIO_UNAVAILABLE);
+
+ try {
+ mSocket.close();
+ } catch (IOException ex) {
+ }
+
+ mSocket = null;
+ RILRequest.resetSerial();
+
+ // Clear request list on close
+ clearRequestsList(RADIO_NOT_AVAILABLE, false);
+ }} catch (Throwable tr) {
+ Log.e(LOG_TAG,"Uncaught exception", tr);
+ }
+
+ /* We're disconnected so we don't know the ril version */
+ notifyRegistrantsRilConnectionChanged(-1);
+ }
+ }
+
+
+
+ //***** Constructors
+
+ public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
+ super(context);
+ if (RILJ_LOGD) {
+ riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
+ " cdmaSubscription=" + cdmaSubscription + ")");
+ }
+ mCdmaSubscription = cdmaSubscription;
+ mPreferredNetworkType = preferredNetworkType;
+ mPhoneType = RILConstants.NO_PHONE;
+
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+ mWakeLock.setReferenceCounted(false);
+ mWakeLockTimeout = SystemProperties.getInt(TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT,
+ DEFAULT_WAKE_LOCK_TIMEOUT);
+ mRequestMessagesPending = 0;
+ mRequestMessagesWaiting = 0;
+
+ mSenderThread = new HandlerThread("RILSender");
+ mSenderThread.start();
+
+ Looper looper = mSenderThread.getLooper();
+ mSender = new RILSender(looper);
+
+ ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
+ riljLog("Not starting RILReceiver: wifi-only");
+ } else {
+ riljLog("Starting RILReceiver");
+ mReceiver = new RILReceiver();
+ mReceiverThread = new Thread(mReceiver, "RILReceiver");
+ mReceiverThread.start();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mIntentReceiver, filter);
+ }
+ }
+
+ //***** CommandsInterface implementation
+
+ public void getVoiceRadioTechnology(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_VOICE_RADIO_TECH, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+
+ @Override public void
+ setOnNITZTime(Handler h, int what, Object obj) {
+ super.setOnNITZTime(h, what, obj);
+
+ // Send the last NITZ time if we have it
+ if (mLastNITZTimeInfo != null) {
+ mNITZTimeRegistrant
+ .notifyRegistrant(
+ new AsyncResult (null, mLastNITZTimeInfo, null));
+ mLastNITZTimeInfo = null;
+ }
+ }
+
+ public void
+ getIccCardStatus(Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ @Override public void
+ supplyIccPin(String pin, Message result) {
+ supplyIccPinForApp(pin, null, result);
+ }
+
+ @Override public void
+ supplyIccPinForApp(String pin, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(2);
+ rr.mp.writeString(pin);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ @Override public void
+ supplyIccPuk(String puk, String newPin, Message result) {
+ supplyIccPukForApp(puk, newPin, null, result);
+ }
+
+ @Override public void
+ supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(puk);
+ rr.mp.writeString(newPin);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ @Override public void
+ supplyIccPin2(String pin, Message result) {
+ supplyIccPin2ForApp(pin, null, result);
+ }
+
+ @Override public void
+ supplyIccPin2ForApp(String pin, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(2);
+ rr.mp.writeString(pin);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ @Override public void
+ supplyIccPuk2(String puk2, String newPin2, Message result) {
+ supplyIccPuk2ForApp(puk2, newPin2, null, result);
+ }
+
+ @Override public void
+ supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(puk);
+ rr.mp.writeString(newPin2);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ @Override public void
+ changeIccPin(String oldPin, String newPin, Message result) {
+ changeIccPinForApp(oldPin, newPin, null, result);
+ }
+
+ @Override public void
+ changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(oldPin);
+ rr.mp.writeString(newPin);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ @Override public void
+ changeIccPin2(String oldPin2, String newPin2, Message result) {
+ changeIccPin2ForApp(oldPin2, newPin2, null, result);
+ }
+
+ @Override public void
+ changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(oldPin2);
+ rr.mp.writeString(newPin2);
+ rr.mp.writeString(aid);
+
+ send(rr);
+ }
+
+ public void
+ changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(facility);
+ rr.mp.writeString(oldPwd);
+ rr.mp.writeString(newPwd);
+
+ send(rr);
+ }
+
+ public void
+ supplyNetworkDepersonalization(String netpin, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeInt(1);
+ rr.mp.writeString(netpin);
+
+ send(rr);
+ }
+
+ public void
+ getCurrentCalls (Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ @Deprecated public void
+ getPDPContextList(Message result) {
+ getDataCallList(result);
+ }
+
+ public void
+ getDataCallList(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DATA_CALL_LIST, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ dial (String address, int clirMode, Message result) {
+ dial(address, clirMode, null, result);
+ }
+
+ public void
+ dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
+
+ rr.mp.writeString(address);
+ rr.mp.writeInt(clirMode);
+ rr.mp.writeInt(0); // UUS information is absent
+
+ if (uusInfo == null) {
+ rr.mp.writeInt(0); // UUS information is absent
+ } else {
+ rr.mp.writeInt(1); // UUS information is present
+ rr.mp.writeInt(uusInfo.getType());
+ rr.mp.writeInt(uusInfo.getDcs());
+ rr.mp.writeByteArray(uusInfo.getUserData());
+ }
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getIMSI(Message result) {
+ getIMSIForApp(null, result);
+ }
+
+ public void
+ getIMSIForApp(String aid, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeString(aid);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() +
+ "> getIMSI: " + requestToString(rr.mRequest)
+ + " aid: " + aid);
+
+ send(rr);
+ }
+
+ public void
+ getIMEI(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getIMEISV(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEISV, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+
+ public void
+ hangupConnection (int gsmIndex, Message result) {
+ if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);
+
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
+ gsmIndex);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(gsmIndex);
+
+ send(rr);
+ }
+
+ public void
+ hangupWaitingOrBackground (Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
+ result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ hangupForegroundResumeBackground (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(
+ RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
+ result);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ switchWaitingOrHoldingAndActive (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(
+ RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
+ result);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ conference (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_CONFERENCE, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+
+ public void setPreferredVoicePrivacy(boolean enable, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE,
+ result);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(enable ? 1:0);
+
+ send(rr);
+ }
+
+ public void getPreferredVoicePrivacy(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
+ result);
+ send(rr);
+ }
+
+ public void
+ separateConnection (int gsmIndex, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SEPARATE_CONNECTION, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + gsmIndex);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(gsmIndex);
+
+ send(rr);
+ }
+
+ public void
+ acceptCall (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_ANSWER, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ rejectCall (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_UDUB, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ explicitCallTransfer (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getLastCallFailCause (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * @deprecated
+ */
+ public void
+ getLastPdpFailCause (Message result) {
+ getLastDataCallFailCause (result);
+ }
+
+ /**
+ * The preferred new alternative to getLastPdpFailCause
+ */
+ public void
+ getLastDataCallFailCause (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ setMute (boolean enableMute, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_MUTE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + enableMute);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(enableMute ? 1 : 0);
+
+ send(rr);
+ }
+
+ public void
+ getMute (Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_GET_MUTE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getSignalStrength (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SIGNAL_STRENGTH, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getVoiceRegistrationState (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_VOICE_REGISTRATION_STATE, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getDataRegistrationState (Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_DATA_REGISTRATION_STATE, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getOperator(Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_OPERATOR, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ sendDtmf(char c, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_DTMF, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeString(Character.toString(c));
+
+ send(rr);
+ }
+
+ public void
+ startDtmf(char c, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_DTMF_START, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeString(Character.toString(c));
+
+ send(rr);
+ }
+
+ public void
+ stopDtmf(Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_DTMF_STOP, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ sendBurstDtmf(String dtmfString, int on, int off, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BURST_DTMF, result);
+
+ rr.mp.writeInt(3);
+ rr.mp.writeString(dtmfString);
+ rr.mp.writeString(Integer.toString(on));
+ rr.mp.writeString(Integer.toString(off));
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + dtmfString);
+
+ send(rr);
+ }
+
+ public void
+ sendSMS (String smscPDU, String pdu, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
+
+ rr.mp.writeInt(2);
+ rr.mp.writeString(smscPDU);
+ rr.mp.writeString(pdu);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ sendCdmaSms(byte[] pdu, Message result) {
+ int address_nbr_of_digits;
+ int subaddr_nbr_of_digits;
+ int bearerDataLength;
+ ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
+ DataInputStream dis = new DataInputStream(bais);
+
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_CDMA_SEND_SMS, result);
+
+ try {
+ rr.mp.writeInt(dis.readInt()); //teleServiceId
+ rr.mp.writeByte((byte) dis.readInt()); //servicePresent
+ rr.mp.writeInt(dis.readInt()); //serviceCategory
+ rr.mp.writeInt(dis.read()); //address_digit_mode
+ rr.mp.writeInt(dis.read()); //address_nbr_mode
+ rr.mp.writeInt(dis.read()); //address_ton
+ rr.mp.writeInt(dis.read()); //address_nbr_plan
+ address_nbr_of_digits = (byte) dis.read();
+ rr.mp.writeByte((byte) address_nbr_of_digits);
+ for(int i=0; i < address_nbr_of_digits; i++){
+ rr.mp.writeByte(dis.readByte()); // address_orig_bytes[i]
+ }
+ rr.mp.writeInt(dis.read()); //subaddressType
+ rr.mp.writeByte((byte) dis.read()); //subaddr_odd
+ subaddr_nbr_of_digits = (byte) dis.read();
+ rr.mp.writeByte((byte) subaddr_nbr_of_digits);
+ for(int i=0; i < subaddr_nbr_of_digits; i++){
+ rr.mp.writeByte(dis.readByte()); //subaddr_orig_bytes[i]
+ }
+
+ bearerDataLength = dis.read();
+ rr.mp.writeInt(bearerDataLength);
+ for(int i=0; i < bearerDataLength; i++){
+ rr.mp.writeByte(dis.readByte()); //bearerData[i]
+ }
+ }catch (IOException ex){
+ if (RILJ_LOGD) riljLog("sendSmsCdma: conversion from input stream to object failed: "
+ + ex);
+ }
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void deleteSmsOnSim(int index, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DELETE_SMS_ON_SIM,
+ response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(index);
+
+ if (false) {
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest)
+ + " " + index);
+ }
+
+ send(rr);
+ }
+
+ public void deleteSmsOnRuim(int index, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM,
+ response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(index);
+
+ if (false) {
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest)
+ + " " + index);
+ }
+
+ send(rr);
+ }
+
+ public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
+ status = translateStatus(status);
+
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_WRITE_SMS_TO_SIM,
+ response);
+
+ rr.mp.writeInt(status);
+ rr.mp.writeString(pdu);
+ rr.mp.writeString(smsc);
+
+ if (false) {
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest)
+ + " " + status);
+ }
+
+ send(rr);
+ }
+
+ public void writeSmsToRuim(int status, String pdu, Message response) {
+ status = translateStatus(status);
+
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM,
+ response);
+
+ rr.mp.writeInt(status);
+ rr.mp.writeString(pdu);
+
+ if (false) {
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest)
+ + " " + status);
+ }
+
+ send(rr);
+ }
+
+ /**
+ * Translates EF_SMS status bits to a status value compatible with
+ * SMS AT commands. See TS 27.005 3.1.
+ */
+ private int translateStatus(int status) {
+ switch(status & 0x7) {
+ case SmsManager.STATUS_ON_ICC_READ:
+ return 1;
+ case SmsManager.STATUS_ON_ICC_UNREAD:
+ return 0;
+ case SmsManager.STATUS_ON_ICC_SENT:
+ return 3;
+ case SmsManager.STATUS_ON_ICC_UNSENT:
+ return 2;
+ }
+
+ // Default to READ.
+ return 1;
+ }
+
+ public void
+ setupDataCall(String radioTechnology, String profile, String apn,
+ String user, String password, String authType, String protocol,
+ Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
+
+ rr.mp.writeInt(7);
+
+ rr.mp.writeString(radioTechnology);
+ rr.mp.writeString(profile);
+ rr.mp.writeString(apn);
+ rr.mp.writeString(user);
+ rr.mp.writeString(password);
+ rr.mp.writeString(authType);
+ rr.mp.writeString(protocol);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest) + " " + radioTechnology + " "
+ + profile + " " + apn + " " + user + " "
+ + password + " " + authType + " " + protocol);
+
+ send(rr);
+ }
+
+ public void
+ deactivateDataCall(int cid, int reason, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result);
+
+ rr.mp.writeInt(2);
+ rr.mp.writeString(Integer.toString(cid));
+ rr.mp.writeString(Integer.toString(reason));
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " +
+ requestToString(rr.mRequest) + " " + cid + " " + reason);
+
+ send(rr);
+ }
+
+ public void
+ setRadioPower(boolean on, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(on ? 1 : 0);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + (on ? " on" : " off"));
+ }
+
+ send(rr);
+ }
+
+ public void
+ setSuppServiceNotifications(boolean enable, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(enable ? 1 : 0);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result);
+
+ rr.mp.writeInt(2);
+ rr.mp.writeInt(success ? 1 : 0);
+ rr.mp.writeInt(cause);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + success + " " + cause);
+
+ send(rr);
+ }
+
+ public void
+ acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result);
+
+ rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass
+ // cause code according to X.S004-550E
+ rr.mp.writeInt(cause);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + success + " " + cause);
+
+ send(rr);
+ }
+
+ public void
+ acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result);
+
+ rr.mp.writeInt(2);
+ rr.mp.writeString(success ? "1" : "0");
+ rr.mp.writeString(ackPdu);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + ' ' + success + " [" + ackPdu + ']');
+
+ send(rr);
+ }
+
+ public void
+ iccIO (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, Message result) {
+ iccIOForApp(command, fileid, path, p1, p2, p3, data, pin2, null, result);
+ }
+ public void
+ iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, String aid, Message result) {
+ //Note: This RIL request has not been renamed to ICC,
+ // but this request is also valid for SIM and RUIM
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SIM_IO, result);
+
+ rr.mp.writeInt(command);
+ rr.mp.writeInt(fileid);
+ rr.mp.writeString(path);
+ rr.mp.writeInt(p1);
+ rr.mp.writeInt(p2);
+ rr.mp.writeInt(p3);
+ rr.mp.writeString(data);
+ rr.mp.writeString(pin2);
+ rr.mp.writeString(aid);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: "
+ + requestToString(rr.mRequest)
+ + " 0x" + Integer.toHexString(command)
+ + " 0x" + Integer.toHexString(fileid) + " "
+ + " path: " + path + ","
+ + p1 + "," + p2 + "," + p3
+ + " aid: " + aid);
+
+ send(rr);
+ }
+
+ public void
+ getCLIR(Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_GET_CLIR, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ setCLIR(int clirMode, Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_CLIR, result);
+
+ // count ints
+ rr.mp.writeInt(1);
+
+ rr.mp.writeInt(clirMode);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + clirMode);
+
+ send(rr);
+ }
+
+ public void
+ queryCallWaiting(int serviceClass, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_WAITING, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(serviceClass);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + serviceClass);
+
+ send(rr);
+ }
+
+ public void
+ setCallWaiting(boolean enable, int serviceClass, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_CALL_WAITING, response);
+
+ rr.mp.writeInt(2);
+ rr.mp.writeInt(enable ? 1 : 0);
+ rr.mp.writeInt(serviceClass);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + enable + ", " + serviceClass);
+
+ send(rr);
+ }
+
+ public void
+ setNetworkSelectionModeAutomatic(Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ setNetworkSelectionModeManual(String operatorNumeric, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + operatorNumeric);
+
+ rr.mp.writeString(operatorNumeric);
+
+ send(rr);
+ }
+
+ public void
+ getNetworkSelectionMode(Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getAvailableNetworks(Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ setCallForward(int action, int cfReason, int serviceClass,
+ String number, int timeSeconds, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_CALL_FORWARD, response);
+
+ rr.mp.writeInt(action);
+ rr.mp.writeInt(cfReason);
+ rr.mp.writeInt(serviceClass);
+ rr.mp.writeInt(PhoneNumberUtils.toaFromString(number));
+ rr.mp.writeString(number);
+ rr.mp.writeInt (timeSeconds);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + action + " " + cfReason + " " + serviceClass
+ + timeSeconds);
+
+ send(rr);
+ }
+
+ public void
+ queryCallForwardStatus(int cfReason, int serviceClass,
+ String number, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, response);
+
+ rr.mp.writeInt(2); // 2 is for query action, not in used anyway
+ rr.mp.writeInt(cfReason);
+ rr.mp.writeInt(serviceClass);
+ rr.mp.writeInt(PhoneNumberUtils.toaFromString(number));
+ rr.mp.writeString(number);
+ rr.mp.writeInt (0);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + cfReason + " " + serviceClass);
+
+ send(rr);
+ }
+
+ public void
+ queryCLIP(Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_CLIP, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+
+ public void
+ getBasebandVersion (Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_BASEBAND_VERSION, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ @Override
+ public void
+ queryFacilityLock(String facility, String password, int serviceClass,
+ Message response) {
+ queryFacilityLockForApp(facility, password, serviceClass, null, response);
+ }
+
+ @Override
+ public void
+ queryFacilityLockForApp(String facility, String password, int serviceClass, String appId,
+ Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_FACILITY_LOCK, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ // count strings
+ rr.mp.writeInt(4);
+
+ rr.mp.writeString(facility);
+ rr.mp.writeString(password);
+
+ rr.mp.writeString(Integer.toString(serviceClass));
+ rr.mp.writeString(appId);
+
+ send(rr);
+ }
+
+ @Override
+ public void
+ setFacilityLock (String facility, boolean lockState, String password,
+ int serviceClass, Message response) {
+ setFacilityLockForApp(facility, lockState, password, serviceClass, null, response);
+ }
+
+ @Override
+ public void
+ setFacilityLockForApp(String facility, boolean lockState, String password,
+ int serviceClass, String appId, Message response) {
+ String lockString;
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ // count strings
+ rr.mp.writeInt(5);
+
+ rr.mp.writeString(facility);
+ lockString = (lockState)?"1":"0";
+ rr.mp.writeString(lockString);
+ rr.mp.writeString(password);
+ rr.mp.writeString(Integer.toString(serviceClass));
+ rr.mp.writeString(appId);
+
+ send(rr);
+
+ }
+
+ public void
+ sendUSSD (String ussdString, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SEND_USSD, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + ussdString);
+
+ rr.mp.writeString(ussdString);
+
+ send(rr);
+ }
+
+ // inherited javadoc suffices
+ public void cancelPendingUssd (Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_CANCEL_USSD, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString()
+ + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+
+ public void resetRadio(Message result) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_RESET_RADIO, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + "[" + IccUtils.bytesToHexString(data) + "]");
+
+ rr.mp.writeByteArray(data);
+
+ send(rr);
+
+ }
+
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeStringArray(strings);
+
+ send(rr);
+ }
+
+ /**
+ * Assign a specified band for RF configuration.
+ *
+ * @param bandMode one of BM_*_BAND
+ * @param response is callback message
+ */
+ public void setBandMode (int bandMode, Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_SET_BAND_MODE, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(bandMode);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + bandMode);
+
+ send(rr);
+ }
+
+ /**
+ * Query the list of band mode supported by RF.
+ *
+ * @param response is callback message
+ * ((AsyncResult)response.obj).result is an int[] with every
+ * element representing one avialable BM_*_BAND
+ */
+ public void queryAvailableBandMode (Message response) {
+ RILRequest rr
+ = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sendTerminalResponse(String contents, Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeString(contents);
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sendEnvelope(String contents, Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ rr.mp.writeString(contents);
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sendEnvelopeWithStatus(String contents, Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + '[' + contents + ']');
+
+ rr.mp.writeString(contents);
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleCallSetupRequestFromSim(
+ boolean accept, Message response) {
+
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
+ response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ int[] param = new int[1];
+ param[0] = accept ? 1 : 0;
+ rr.mp.writeIntArray(param);
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setCurrentPreferredNetworkType() {
+ if (RILJ_LOGD) riljLog("setCurrentPreferredNetworkType: " + mSetPreferredNetworkType);
+ setPreferredNetworkType(mSetPreferredNetworkType, null);
+ }
+ private int mSetPreferredNetworkType;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setPreferredNetworkType(int networkType , Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(networkType);
+
+ mSetPreferredNetworkType = networkType;
+ mPreferredNetworkType = networkType;
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + networkType);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void getPreferredNetworkType(Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void getNeighboringCids(Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setLocationUpdates(boolean enable, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_LOCATION_UPDATES, response);
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(enable ? 1 : 0);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest) + ": " + enable);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void getSmscAddress(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SMSC_ADDRESS, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setSmscAddress(String address, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_SMSC_ADDRESS, result);
+
+ rr.mp.writeString(address);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + address);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void reportSmsMemoryStatus(boolean available, Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result);
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(available ? 1 : 0);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest) + ": " + available);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void reportStkServiceIsRunning(Message result) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void getGsmBroadcastConfig(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, response);
+
+ int numOfConfig = config.length;
+ rr.mp.writeInt(numOfConfig);
+
+ for(int i = 0; i < numOfConfig; i++) {
+ rr.mp.writeInt(config[i].getFromServiceId());
+ rr.mp.writeInt(config[i].getToServiceId());
+ rr.mp.writeInt(config[i].getFromCodeScheme());
+ rr.mp.writeInt(config[i].getToCodeScheme());
+ rr.mp.writeInt(config[i].isSelected() ? 1 : 0);
+ }
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " with " + numOfConfig + " configs : ");
+ for (int i = 0; i < numOfConfig; i++) {
+ riljLog(config[i].toString());
+ }
+ }
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setGsmBroadcastActivation(boolean activate, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(activate ? 0 : 1);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ //***** Private Methods
+
+ private void sendScreenState(boolean on) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_SCREEN_STATE, null);
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(on ? 1 : 0);
+
+ if (RILJ_LOGD) riljLog(rr.serialString()
+ + "> " + requestToString(rr.mRequest) + ": " + on);
+
+ send(rr);
+ }
+
+ protected void
+ onRadioAvailable() {
+ // In case screen state was lost (due to process crash),
+ // this ensures that the RIL knows the correct screen state.
+
+ // TODO: Should query Power Manager and send the actual
+ // screen state. Just send true for now.
+ sendScreenState(true);
+ }
+
+ private RadioState getRadioStateFromInt(int stateInt) {
+ RadioState state;
+
+ /* RIL_RadioState ril.h */
+ switch(stateInt) {
+ case 0: state = RadioState.RADIO_OFF; break;
+ case 1: state = RadioState.RADIO_UNAVAILABLE; break;
+ case 10: state = RadioState.RADIO_ON; break;
+
+ default:
+ throw new RuntimeException(
+ "Unrecognized RIL_RadioState: " + stateInt);
+ }
+ return state;
+ }
+
+ private void switchToRadioState(RadioState newState) {
+ setRadioState(newState);
+ }
+
+ /**
+ * Holds a PARTIAL_WAKE_LOCK whenever
+ * a) There is outstanding RIL request sent to RIL deamon and no replied
+ * b) There is a request pending to be sent out.
+ *
+ * There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't
+ * happen often.
+ */
+
+ private void
+ acquireWakeLock() {
+ synchronized (mWakeLock) {
+ mWakeLock.acquire();
+ mRequestMessagesPending++;
+
+ mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
+ Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
+ mSender.sendMessageDelayed(msg, mWakeLockTimeout);
+ }
+ }
+
+ private void
+ releaseWakeLockIfDone() {
+ synchronized (mWakeLock) {
+ if (mWakeLock.isHeld() &&
+ (mRequestMessagesPending == 0) &&
+ (mRequestMessagesWaiting == 0)) {
+ mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
+ mWakeLock.release();
+ }
+ }
+ }
+
+ private void
+ send(RILRequest rr) {
+ Message msg;
+
+ if (mSocket == null) {
+ rr.onError(RADIO_NOT_AVAILABLE, null);
+ rr.release();
+ return;
+ }
+
+ msg = mSender.obtainMessage(EVENT_SEND, rr);
+
+ acquireWakeLock();
+
+ msg.sendToTarget();
+ }
+
+ private void
+ processResponse (Parcel p) {
+ int type;
+
+ type = p.readInt();
+
+ if (type == RESPONSE_UNSOLICITED) {
+ processUnsolicited (p);
+ } else if (type == RESPONSE_SOLICITED) {
+ processSolicited (p);
+ }
+
+ releaseWakeLockIfDone();
+ }
+
+ /**
+ * Release each request in mReqeustsList then clear the list
+ * @param error is the RIL_Errno sent back
+ * @param loggable true means to print all requests in mRequestslist
+ */
+ private void clearRequestsList(int error, boolean loggable) {
+ RILRequest rr;
+ synchronized (mRequestsList) {
+ int count = mRequestsList.size();
+ if (RILJ_LOGD && loggable) {
+ Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " +
+ " mReqPending=" + mRequestMessagesPending +
+ " mRequestList=" + count);
+ }
+
+ for (int i = 0; i < count ; i++) {
+ rr = mRequestsList.get(i);
+ if (RILJ_LOGD && loggable) {
+ Log.d(LOG_TAG, i + ": [" + rr.mSerial + "] " +
+ requestToString(rr.mRequest));
+ }
+ rr.onError(error, null);
+ rr.release();
+ }
+ mRequestsList.clear();
+ mRequestMessagesWaiting = 0;
+ }
+ }
+
+ private RILRequest findAndRemoveRequestFromList(int serial) {
+ synchronized (mRequestsList) {
+ for (int i = 0, s = mRequestsList.size() ; i < s ; i++) {
+ RILRequest rr = mRequestsList.get(i);
+
+ if (rr.mSerial == serial) {
+ mRequestsList.remove(i);
+ if (mRequestMessagesWaiting > 0)
+ mRequestMessagesWaiting--;
+ return rr;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private void
+ processSolicited (Parcel p) {
+ int serial, error;
+ boolean found = false;
+
+ serial = p.readInt();
+ error = p.readInt();
+
+ RILRequest rr;
+
+ rr = findAndRemoveRequestFromList(serial);
+
+ if (rr == null) {
+ Log.w(LOG_TAG, "Unexpected solicited response! sn: "
+ + serial + " error: " + error);
+ return;
+ }
+
+ Object ret = null;
+
+ if (error == 0 || p.dataAvail() > 0) {
+ // either command succeeds or command fails but with data payload
+ try {switch (rr.mRequest) {
+ /*
+ cat libs/telephony/ril_commands.h \
+ | egrep "^ *{RIL_" \
+ | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/'
+ */
+ case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break;
+ case RIL_REQUEST_ENTER_SIM_PIN: ret = responseInts(p); break;
+ case RIL_REQUEST_ENTER_SIM_PUK: ret = responseInts(p); break;
+ case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseInts(p); break;
+ case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseInts(p); break;
+ case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseInts(p); break;
+ case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseInts(p); break;
+ case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseInts(p); break;
+ case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break;
+ case RIL_REQUEST_DIAL: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_IMSI: ret = responseString(p); break;
+ case RIL_REQUEST_HANGUP: ret = responseVoid(p); break;
+ case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret = responseVoid(p); break;
+ case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: {
+ if (mTestingEmergencyCall.getAndSet(false)) {
+ if (mEmergencyCallbackModeRegistrant != null) {
+ riljLog("testing emergency call, notify ECM Registrants");
+ mEmergencyCallbackModeRegistrant.notifyRegistrant();
+ }
+ }
+ ret = responseVoid(p);
+ break;
+ }
+ 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_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;
+ case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break;
+ case RIL_REQUEST_RADIO_POWER: ret = responseVoid(p); break;
+ case RIL_REQUEST_DTMF: ret = responseVoid(p); break;
+ case RIL_REQUEST_SEND_SMS: ret = responseSMS(p); break;
+ case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret = responseSMS(p); break;
+ case RIL_REQUEST_SETUP_DATA_CALL: ret = responseSetupDataCall(p); break;
+ case RIL_REQUEST_SIM_IO: ret = responseICC_IO(p); break;
+ case RIL_REQUEST_SEND_USSD: ret = responseVoid(p); break;
+ case RIL_REQUEST_CANCEL_USSD: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_CLIR: ret = responseInts(p); break;
+ case RIL_REQUEST_SET_CLIR: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: ret = responseCallForward(p); break;
+ case RIL_REQUEST_SET_CALL_FORWARD: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_CALL_WAITING: ret = responseInts(p); break;
+ case RIL_REQUEST_SET_CALL_WAITING: ret = responseVoid(p); break;
+ case RIL_REQUEST_SMS_ACKNOWLEDGE: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_IMEI: ret = responseString(p); break;
+ case RIL_REQUEST_GET_IMEISV: ret = responseString(p); break;
+ case RIL_REQUEST_ANSWER: ret = responseVoid(p); break;
+ case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_FACILITY_LOCK: ret = responseInts(p); break;
+ case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseInts(p); break;
+ case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break;
+ case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseOperatorInfos(p); break;
+ case RIL_REQUEST_DTMF_START: ret = responseVoid(p); break;
+ case RIL_REQUEST_DTMF_STOP: ret = responseVoid(p); break;
+ case RIL_REQUEST_BASEBAND_VERSION: ret = responseString(p); break;
+ case RIL_REQUEST_SEPARATE_CONNECTION: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_MUTE: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_MUTE: ret = responseInts(p); break;
+ case RIL_REQUEST_QUERY_CLIP: ret = responseInts(p); break;
+ case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: ret = responseInts(p); break;
+ case RIL_REQUEST_DATA_CALL_LIST: ret = responseDataCallList(p); break;
+ case RIL_REQUEST_RESET_RADIO: ret = responseVoid(p); break;
+ case RIL_REQUEST_OEM_HOOK_RAW: ret = responseRaw(p); break;
+ case RIL_REQUEST_OEM_HOOK_STRINGS: ret = responseStrings(p); break;
+ case RIL_REQUEST_SCREEN_STATE: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: ret = responseVoid(p); break;
+ case RIL_REQUEST_WRITE_SMS_TO_SIM: ret = responseInts(p); break;
+ case RIL_REQUEST_DELETE_SMS_ON_SIM: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_BAND_MODE: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: ret = responseInts(p); break;
+ case RIL_REQUEST_STK_GET_PROFILE: ret = responseString(p); break;
+ case RIL_REQUEST_STK_SET_PROFILE: ret = responseVoid(p); break;
+ case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: ret = responseString(p); break;
+ case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: ret = responseVoid(p); break;
+ case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break;
+ case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break;
+ case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break;
+ case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseGetPreferredNetworkType(p); break;
+ case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
+ case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: ret = responseInts(p); break;
+ case RIL_REQUEST_SET_TTY_MODE: ret = responseVoid(p); break;
+ case RIL_REQUEST_QUERY_TTY_MODE: ret = responseInts(p); break;
+ case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: ret = responseInts(p); break;
+ case RIL_REQUEST_CDMA_FLASH: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_BURST_DTMF: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_SEND_SMS: ret = responseSMS(p); break;
+ case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: ret = responseVoid(p); break;
+ case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: ret = responseGmsBroadcastConfig(p); break;
+ case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: ret = responseVoid(p); break;
+ case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret = responseCdmaBroadcastConfig(p); break;
+ case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_SUBSCRIPTION: ret = responseStrings(p); break;
+ case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: ret = responseInts(p); break;
+ case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: ret = responseVoid(p); break;
+ case RIL_REQUEST_DEVICE_IDENTITY: ret = responseStrings(p); break;
+ case RIL_REQUEST_GET_SMSC_ADDRESS: ret = responseString(p); break;
+ case RIL_REQUEST_SET_SMSC_ADDRESS: ret = responseVoid(p); break;
+ case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
+ case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: ret = responseVoid(p); break;
+ case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: ret = responseVoid(p); break;
+ case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: ret = responseInts(p); break;
+ case RIL_REQUEST_ISIM_AUTHENTICATION: ret = responseString(p); break;
+ case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: ret = responseVoid(p); break;
+ case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: ret = responseICC_IO(p); break;
+ case RIL_REQUEST_VOICE_RADIO_TECH: ret = responseInts(p); break;
+ default:
+ throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
+ //break;
+ }} catch (Throwable tr) {
+ // Exceptions here usually mean invalid RIL responses
+
+ Log.w(LOG_TAG, rr.serialString() + "< "
+ + requestToString(rr.mRequest)
+ + " exception, possible invalid RIL response", tr);
+
+ if (rr.mResult != null) {
+ AsyncResult.forMessage(rr.mResult, null, tr);
+ rr.mResult.sendToTarget();
+ }
+ rr.release();
+ return;
+ }
+ }
+
+ if (error != 0) {
+ rr.onError(error, ret);
+ rr.release();
+ return;
+ }
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+ + " " + retToString(rr.mRequest, ret));
+
+ if (rr.mResult != null) {
+ AsyncResult.forMessage(rr.mResult, ret, null);
+ rr.mResult.sendToTarget();
+ }
+
+ rr.release();
+ }
+
+ private String
+ retToString(int req, Object ret) {
+ if (ret == null) return "";
+ switch (req) {
+ // Don't log these return values, for privacy's sake.
+ case RIL_REQUEST_GET_IMSI:
+ case RIL_REQUEST_GET_IMEI:
+ case RIL_REQUEST_GET_IMEISV:
+ if (!RILJ_LOGV) {
+ // If not versbose logging just return and don't display IMSI and IMEI, IMEISV
+ return "";
+ }
+ }
+
+ StringBuilder sb;
+ String s;
+ int length;
+ if (ret instanceof int[]){
+ int[] intArray = (int[]) ret;
+ length = intArray.length;
+ sb = new StringBuilder("{");
+ if (length > 0) {
+ int i = 0;
+ sb.append(intArray[i++]);
+ while ( i < length) {
+ sb.append(", ").append(intArray[i++]);
+ }
+ }
+ sb.append("}");
+ s = sb.toString();
+ } else if (ret instanceof String[]) {
+ String[] strings = (String[]) ret;
+ length = strings.length;
+ sb = new StringBuilder("{");
+ if (length > 0) {
+ int i = 0;
+ sb.append(strings[i++]);
+ while ( i < length) {
+ sb.append(", ").append(strings[i++]);
+ }
+ }
+ sb.append("}");
+ s = sb.toString();
+ }else if (req == RIL_REQUEST_GET_CURRENT_CALLS) {
+ ArrayList calls = (ArrayList) ret;
+ sb = new StringBuilder(" ");
+ for (DriverCall dc : calls) {
+ sb.append("[").append(dc).append("] ");
+ }
+ s = sb.toString();
+ } else if (req == RIL_REQUEST_GET_NEIGHBORING_CELL_IDS) {
+ ArrayList cells;
+ cells = (ArrayList) ret;
+ sb = new StringBuilder(" ");
+ for (NeighboringCellInfo cell : cells) {
+ sb.append(cell).append(" ");
+ }
+ s = sb.toString();
+ } else {
+ s = ret.toString();
+ }
+ return s;
+ }
+
+ private void
+ processUnsolicited (Parcel p) {
+ int response;
+ Object ret;
+
+ response = p.readInt();
+
+ try {switch(response) {
+/*
+ cat libs/telephony/ril_unsol_commands.h \
+ | egrep "^ *{RIL_" \
+ | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/'
+*/
+
+ case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break;
+ case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break;
+ case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break;
+ case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break;
+ case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break;
+ case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break;
+ case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break;
+ case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break;
+ case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break;
+ case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break;
+ case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break;
+ case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break;
+ case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret = responseVoid(p); break;
+ case RIL_UNSOL_SIM_REFRESH: ret = responseSimRefresh(p); break;
+ case RIL_UNSOL_CALL_RING: ret = responseCallRing(p); break;
+ case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break;
+ case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break;
+ case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseRaw(p); break;
+ case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break;
+ case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
+ case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break;
+ case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break;
+ case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break;
+ case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break;
+ case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break;
+ case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break;
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break;
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
+ case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break;
+ case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: ret = responseInts(p); break;
+
+ default:
+ throw new RuntimeException("Unrecognized unsol response: " + response);
+ //break; (implied)
+ }} catch (Throwable tr) {
+ Log.e(LOG_TAG, "Exception processing unsol response: " + response +
+ "Exception:" + tr.toString());
+ return;
+ }
+
+ switch(response) {
+ case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
+ /* has bonus radio state int */
+ RadioState newState = getRadioStateFromInt(p.readInt());
+ if (RILJ_LOGD) unsljLogMore(response, newState.toString());
+
+ switchToRadioState(newState);
+ break;
+ case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
+ if (RILJ_LOGD) unsljLog(response);
+
+ mCallStateRegistrants
+ .notifyRegistrants(new AsyncResult(null, null, null));
+ break;
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
+ if (RILJ_LOGD) unsljLog(response);
+
+ mVoiceNetworkStateRegistrants
+ .notifyRegistrants(new AsyncResult(null, null, null));
+ break;
+ case RIL_UNSOL_RESPONSE_NEW_SMS: {
+ if (RILJ_LOGD) unsljLog(response);
+
+ // FIXME this should move up a layer
+ String a[] = new String[2];
+
+ a[1] = (String)ret;
+
+ SmsMessage sms;
+
+ sms = SmsMessage.newFromCMT(a);
+ if (mGsmSmsRegistrant != null) {
+ mGsmSmsRegistrant
+ .notifyRegistrant(new AsyncResult(null, sms, null));
+ }
+ break;
+ }
+ case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mSmsStatusRegistrant != null) {
+ mSmsStatusRegistrant.notifyRegistrant(
+ new AsyncResult(null, ret, null));
+ }
+ break;
+ case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ int[] smsIndex = (int[])ret;
+
+ if(smsIndex.length == 1) {
+ if (mSmsOnSimRegistrant != null) {
+ mSmsOnSimRegistrant.
+ notifyRegistrant(new AsyncResult(null, smsIndex, null));
+ }
+ } else {
+ if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length "
+ + smsIndex.length);
+ }
+ break;
+ case RIL_UNSOL_ON_USSD:
+ String[] resp = (String[])ret;
+
+ if (resp.length < 2) {
+ resp = new String[2];
+ resp[0] = ((String[])ret)[0];
+ resp[1] = null;
+ }
+ if (RILJ_LOGD) unsljLogMore(response, resp[0]);
+ if (mUSSDRegistrant != null) {
+ mUSSDRegistrant.notifyRegistrant(
+ new AsyncResult (null, resp, null));
+ }
+ break;
+ case RIL_UNSOL_NITZ_TIME_RECEIVED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ // has bonus long containing milliseconds since boot that the NITZ
+ // time was received
+ long nitzReceiveTime = p.readLong();
+
+ Object[] result = new Object[2];
+
+ result[0] = ret;
+ result[1] = Long.valueOf(nitzReceiveTime);
+
+ boolean ignoreNitz = SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
+
+ if (ignoreNitz) {
+ if (RILJ_LOGD) riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
+ } else {
+ if (mNITZTimeRegistrant != null) {
+
+ mNITZTimeRegistrant
+ .notifyRegistrant(new AsyncResult (null, result, null));
+ } else {
+ // in case NITZ time registrant isnt registered yet
+ mLastNITZTimeInfo = result;
+ }
+ }
+ break;
+
+ case RIL_UNSOL_SIGNAL_STRENGTH:
+ // Note this is set to "verbose" because it happens
+ // frequently
+ if (RILJ_LOGV) unsljLogvRet(response, ret);
+
+ if (mSignalStrengthRegistrant != null) {
+ mSignalStrengthRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+ case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ mDataNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
+ break;
+
+ case RIL_UNSOL_SUPP_SVC_NOTIFICATION:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mSsnRegistrant != null) {
+ mSsnRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_STK_SESSION_END:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mCatSessionEndRegistrant != null) {
+ mCatSessionEndRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_STK_PROACTIVE_COMMAND:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCatProCmdRegistrant != null) {
+ mCatProCmdRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_STK_EVENT_NOTIFY:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCatEventRegistrant != null) {
+ mCatEventRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_STK_CALL_SETUP:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCatCallSetUpRegistrant != null) {
+ mCatCallSetUpRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_SIM_SMS_STORAGE_FULL:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mIccSmsFullRegistrant != null) {
+ mIccSmsFullRegistrant.notifyRegistrant();
+ }
+ break;
+
+ case RIL_UNSOL_SIM_REFRESH:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mIccRefreshRegistrants != null) {
+ mIccRefreshRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_CALL_RING:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mRingRegistrant != null) {
+ mRingRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_RESTRICTED_STATE_CHANGED:
+ if (RILJ_LOGD) unsljLogvRet(response, ret);
+ if (mRestrictedStateRegistrant != null) {
+ mRestrictedStateRegistrant.notifyRegistrant(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mIccStatusChangedRegistrants != null) {
+ mIccStatusChangedRegistrants.notifyRegistrants();
+ }
+ break;
+
+ case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:
+ if (RILJ_LOGD) unsljLog(response);
+
+ SmsMessage sms = (SmsMessage) ret;
+
+ if (mCdmaSmsRegistrant != null) {
+ mCdmaSmsRegistrant
+ .notifyRegistrant(new AsyncResult(null, sms, null));
+ }
+ break;
+
+ case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mGsmBroadcastSmsRegistrant != null) {
+ mGsmBroadcastSmsRegistrant
+ .notifyRegistrant(new AsyncResult(null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mIccSmsFullRegistrant != null) {
+ mIccSmsFullRegistrant.notifyRegistrant();
+ }
+ break;
+
+ case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE:
+ if (RILJ_LOGD) unsljLog(response);
+
+ if (mEmergencyCallbackModeRegistrant != null) {
+ mEmergencyCallbackModeRegistrant.notifyRegistrant();
+ }
+ break;
+
+ case RIL_UNSOL_CDMA_CALL_WAITING:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCallWaitingInfoRegistrants != null) {
+ mCallWaitingInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mOtaProvisionRegistrants != null) {
+ mOtaProvisionRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_CDMA_INFO_REC:
+ ArrayList listInfoRecs;
+
+ try {
+ listInfoRecs = (ArrayList)ret;
+ } catch (ClassCastException e) {
+ Log.e(LOG_TAG, "Unexpected exception casting to listInfoRecs", e);
+ break;
+ }
+
+ for (CdmaInformationRecords rec : listInfoRecs) {
+ if (RILJ_LOGD) unsljLogRet(response, rec);
+ notifyRegistrantsCdmaInfoRec(rec);
+ }
+ break;
+
+ case RIL_UNSOL_OEM_HOOK_RAW:
+ if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
+ if (mUnsolOemHookRawRegistrant != null) {
+ mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_RINGBACK_TONE:
+ if (RILJ_LOGD) unsljLogvRet(response, ret);
+ if (mRingbackToneRegistrants != null) {
+ boolean playtone = (((int[])ret)[0] == 1);
+ mRingbackToneRegistrants.notifyRegistrants(
+ new AsyncResult (null, playtone, null));
+ }
+ break;
+
+ case RIL_UNSOL_RESEND_INCALL_MUTE:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mResendIncallMuteRegistrants != null) {
+ mResendIncallMuteRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mVoiceRadioTechChangedRegistrants != null) {
+ mVoiceRadioTechChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCdmaSubscriptionChangedRegistrants != null) {
+ mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOl_CDMA_PRL_CHANGED:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mCdmaPrlChangedRegistrants != null) {
+ mCdmaPrlChangedRegistrants.notifyRegistrants(
+ new AsyncResult (null, ret, null));
+ }
+ break;
+
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ if (mExitEmergencyCallbackModeRegistrants != null) {
+ mExitEmergencyCallbackModeRegistrants.notifyRegistrants(
+ new AsyncResult (null, null, null));
+ }
+ break;
+
+ case RIL_UNSOL_RIL_CONNECTED: {
+ if (RILJ_LOGD) unsljLogRet(response, ret);
+
+ // Initial conditions
+ setRadioPower(false, null);
+ setPreferredNetworkType(mPreferredNetworkType, null);
+ setCdmaSubscriptionSource(mCdmaSubscription, null);
+ notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Notifiy all registrants that the ril has connected or disconnected.
+ *
+ * @param rilVer is the version of the ril or -1 if disconnected.
+ */
+ private void notifyRegistrantsRilConnectionChanged(int rilVer) {
+ mRilVersion = rilVer;
+ if (mRilConnectedRegistrants != null) {
+ mRilConnectedRegistrants.notifyRegistrants(
+ new AsyncResult (null, new Integer(rilVer), null));
+ }
+ }
+
+ private Object
+ responseInts(Parcel p) {
+ int numInts;
+ int response[];
+
+ numInts = p.readInt();
+
+ response = new int[numInts];
+
+ for (int i = 0 ; i < numInts ; i++) {
+ response[i] = p.readInt();
+ }
+
+ return response;
+ }
+
+
+ private Object
+ responseVoid(Parcel p) {
+ return null;
+ }
+
+ private Object
+ responseCallForward(Parcel p) {
+ int numInfos;
+ CallForwardInfo infos[];
+
+ numInfos = p.readInt();
+
+ infos = new CallForwardInfo[numInfos];
+
+ for (int i = 0 ; i < numInfos ; i++) {
+ infos[i] = new CallForwardInfo();
+
+ infos[i].status = p.readInt();
+ infos[i].reason = p.readInt();
+ infos[i].serviceClass = p.readInt();
+ infos[i].toa = p.readInt();
+ infos[i].number = p.readString();
+ infos[i].timeSeconds = p.readInt();
+ }
+
+ return infos;
+ }
+
+ private Object
+ responseSuppServiceNotification(Parcel p) {
+ SuppServiceNotification notification = new SuppServiceNotification();
+
+ notification.notificationType = p.readInt();
+ notification.code = p.readInt();
+ notification.index = p.readInt();
+ notification.type = p.readInt();
+ notification.number = p.readString();
+
+ return notification;
+ }
+
+ private Object
+ responseCdmaSms(Parcel p) {
+ SmsMessage sms;
+ sms = SmsMessage.newFromParcel(p);
+
+ return sms;
+ }
+
+ private Object
+ responseString(Parcel p) {
+ String response;
+
+ response = p.readString();
+
+ return response;
+ }
+
+ private Object
+ responseStrings(Parcel p) {
+ int num;
+ String response[];
+
+ response = p.readStringArray();
+
+ if (false) {
+ num = p.readInt();
+
+ response = new String[num];
+ for (int i = 0; i < num; i++) {
+ response[i] = p.readString();
+ }
+ }
+
+ return response;
+ }
+
+ private Object
+ responseRaw(Parcel p) {
+ int num;
+ byte response[];
+
+ response = p.createByteArray();
+
+ return response;
+ }
+
+ private Object
+ responseSMS(Parcel p) {
+ int messageRef, errorCode;
+ String ackPDU;
+
+ messageRef = p.readInt();
+ ackPDU = p.readString();
+ errorCode = p.readInt();
+
+ SmsResponse response = new SmsResponse(messageRef, ackPDU, errorCode);
+
+ return response;
+ }
+
+
+ private Object
+ responseICC_IO(Parcel p) {
+ int sw1, sw2;
+ byte data[] = null;
+ Message ret;
+
+ sw1 = p.readInt();
+ sw2 = p.readInt();
+
+ String s = p.readString();
+
+ if (RILJ_LOGV) riljLog("< iccIO: "
+ + " 0x" + Integer.toHexString(sw1)
+ + " 0x" + Integer.toHexString(sw2) + " "
+ + s);
+
+ return new IccIoResult(sw1, sw2, s);
+ }
+
+ private Object
+ responseIccCardStatus(Parcel p) {
+ IccCardApplication ca;
+
+ IccCardStatus status = new IccCardStatus();
+ status.setCardState(p.readInt());
+ status.setUniversalPinState(p.readInt());
+ status.setGsmUmtsSubscriptionAppIndex(p.readInt());
+ status.setCdmaSubscriptionAppIndex(p.readInt());
+ status.setImsSubscriptionAppIndex(p.readInt());
+ int numApplications = p.readInt();
+
+ // limit to maximum allowed applications
+ if (numApplications > IccCardStatus.CARD_MAX_APPS) {
+ numApplications = IccCardStatus.CARD_MAX_APPS;
+ }
+ status.setNumApplications(numApplications);
+
+ for (int i = 0 ; i < numApplications ; i++) {
+ ca = new IccCardApplication();
+ ca.app_type = ca.AppTypeFromRILInt(p.readInt());
+ ca.app_state = ca.AppStateFromRILInt(p.readInt());
+ ca.perso_substate = ca.PersoSubstateFromRILInt(p.readInt());
+ ca.aid = p.readString();
+ ca.app_label = p.readString();
+ ca.pin1_replaced = p.readInt();
+ ca.pin1 = ca.PinStateFromRILInt(p.readInt());
+ ca.pin2 = ca.PinStateFromRILInt(p.readInt());
+ status.addApplication(ca);
+ }
+ return status;
+ }
+
+ private Object
+ responseSimRefresh(Parcel p) {
+ IccRefreshResponse response = new IccRefreshResponse();
+
+ response.refreshResult = p.readInt();
+ response.efId = p.readInt();
+ response.aid = p.readString();
+ return response;
+ }
+
+ private Object
+ responseCallList(Parcel p) {
+ int num;
+ int voiceSettings;
+ ArrayList response;
+ DriverCall dc;
+
+ num = p.readInt();
+ response = new ArrayList(num);
+
+ if (RILJ_LOGV) {
+ riljLog("responseCallList: num=" + num +
+ " mEmergencyCallbackModeRegistrant=" + mEmergencyCallbackModeRegistrant +
+ " mTestingEmergencyCall=" + mTestingEmergencyCall.get());
+ }
+ for (int i = 0 ; i < num ; i++) {
+ dc = new DriverCall();
+
+ dc.state = DriverCall.stateFromCLCC(p.readInt());
+ dc.index = p.readInt();
+ dc.TOA = p.readInt();
+ dc.isMpty = (0 != p.readInt());
+ dc.isMT = (0 != p.readInt());
+ dc.als = p.readInt();
+ voiceSettings = p.readInt();
+ dc.isVoice = (0 == voiceSettings) ? false : true;
+ dc.isVoicePrivacy = (0 != p.readInt());
+ dc.number = p.readString();
+ int np = p.readInt();
+ dc.numberPresentation = DriverCall.presentationFromCLIP(np);
+ dc.name = p.readString();
+ dc.namePresentation = p.readInt();
+ int uusInfoPresent = p.readInt();
+ if (uusInfoPresent == 1) {
+ dc.uusInfo = new UUSInfo();
+ dc.uusInfo.setType(p.readInt());
+ dc.uusInfo.setDcs(p.readInt());
+ byte[] userData = p.createByteArray();
+ dc.uusInfo.setUserData(userData);
+ riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+ dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+ dc.uusInfo.getUserData().length));
+ riljLogv("Incoming UUS : data (string)="
+ + new String(dc.uusInfo.getUserData()));
+ riljLogv("Incoming UUS : data (hex): "
+ + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+ } else {
+ riljLogv("Incoming UUS : NOT present!");
+ }
+
+ // Make sure there's a leading + on addresses with a TOA of 145
+ dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+
+ response.add(dc);
+
+ if (dc.isVoicePrivacy) {
+ mVoicePrivacyOnRegistrants.notifyRegistrants();
+ riljLog("InCall VoicePrivacy is enabled");
+ } else {
+ mVoicePrivacyOffRegistrants.notifyRegistrants();
+ riljLog("InCall VoicePrivacy is disabled");
+ }
+ }
+
+ Collections.sort(response);
+
+ if ((num == 0) && mTestingEmergencyCall.getAndSet(false)) {
+ if (mEmergencyCallbackModeRegistrant != null) {
+ riljLog("responseCallList: call ended, testing emergency call," +
+ " notify ECM Registrants");
+ mEmergencyCallbackModeRegistrant.notifyRegistrant();
+ }
+ }
+
+ return response;
+ }
+
+ private DataCallState getDataCallState(Parcel p, int version) {
+ DataCallState dataCall = new DataCallState();
+
+ dataCall.version = version;
+ if (version < 5) {
+ dataCall.cid = p.readInt();
+ dataCall.active = p.readInt();
+ dataCall.type = p.readString();
+ String addresses = p.readString();
+ if (!TextUtils.isEmpty(addresses)) {
+ dataCall.addresses = addresses.split(" ");
+ }
+ } else {
+ dataCall.status = p.readInt();
+ dataCall.suggestedRetryTime = p.readInt();
+ dataCall.cid = p.readInt();
+ dataCall.active = p.readInt();
+ dataCall.type = p.readString();
+ dataCall.ifname = p.readString();
+ if ((dataCall.status == DataConnection.FailCause.NONE.getErrorCode()) &&
+ TextUtils.isEmpty(dataCall.ifname)) {
+ throw new RuntimeException("getDataCallState, no ifname");
+ }
+ String addresses = p.readString();
+ if (!TextUtils.isEmpty(addresses)) {
+ dataCall.addresses = addresses.split(" ");
+ }
+ String dnses = p.readString();
+ if (!TextUtils.isEmpty(dnses)) {
+ dataCall.dnses = dnses.split(" ");
+ }
+ String gateways = p.readString();
+ if (!TextUtils.isEmpty(gateways)) {
+ dataCall.gateways = gateways.split(" ");
+ }
+ }
+ return dataCall;
+ }
+
+ private Object
+ responseDataCallList(Parcel p) {
+ ArrayList response;
+
+ int ver = p.readInt();
+ int num = p.readInt();
+ riljLog("responseDataCallList ver=" + ver + " num=" + num);
+
+ response = new ArrayList(num);
+ for (int i = 0; i < num; i++) {
+ response.add(getDataCallState(p, ver));
+ }
+
+ return response;
+ }
+
+ private Object
+ responseSetupDataCall(Parcel p) {
+ int ver = p.readInt();
+ int num = p.readInt();
+ if (RILJ_LOGV) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
+
+ DataCallState dataCall;
+
+ if (ver < 5) {
+ dataCall = new DataCallState();
+ dataCall.version = ver;
+ dataCall.cid = Integer.parseInt(p.readString());
+ dataCall.ifname = p.readString();
+ if (TextUtils.isEmpty(dataCall.ifname)) {
+ throw new RuntimeException(
+ "RIL_REQUEST_SETUP_DATA_CALL response, no ifname");
+ }
+ String addresses = p.readString();
+ if (!TextUtils.isEmpty(addresses)) {
+ dataCall.addresses = addresses.split(" ");
+ }
+ if (num >= 4) {
+ String dnses = p.readString();
+ if (RILJ_LOGD) riljLog("responseSetupDataCall got dnses=" + dnses);
+ if (!TextUtils.isEmpty(dnses)) {
+ dataCall.dnses = dnses.split(" ");
+ }
+ }
+ if (num >= 5) {
+ String gateways = p.readString();
+ if (RILJ_LOGD) riljLog("responseSetupDataCall got gateways=" + gateways);
+ if (!TextUtils.isEmpty(gateways)) {
+ dataCall.gateways = gateways.split(" ");
+ }
+ }
+ } else {
+ if (num != 1) {
+ throw new RuntimeException(
+ "RIL_REQUEST_SETUP_DATA_CALL response expecting 1 RIL_Data_Call_response_v5"
+ + " got " + num);
+ }
+ dataCall = getDataCallState(p, ver);
+ }
+
+ return dataCall;
+ }
+
+ private Object
+ responseOperatorInfos(Parcel p) {
+ String strings[] = (String [])responseStrings(p);
+ ArrayList ret;
+
+ if (strings.length % 4 != 0) {
+ throw new RuntimeException(
+ "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: invalid response. Got "
+ + strings.length + " strings, expected multible of 4");
+ }
+
+ ret = new ArrayList(strings.length / 4);
+
+ for (int i = 0 ; i < strings.length ; i += 4) {
+ ret.add (
+ new OperatorInfo(
+ strings[i+0],
+ strings[i+1],
+ strings[i+2],
+ strings[i+3]));
+ }
+
+ return ret;
+ }
+
+ private Object
+ responseCellList(Parcel p) {
+ int num, rssi;
+ String location;
+ ArrayList response;
+ NeighboringCellInfo cell;
+
+ num = p.readInt();
+ response = new ArrayList();
+
+ // Determine the radio access type
+ String radioString = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, "unknown");
+ int radioType;
+ if (radioString.equals("GPRS")) {
+ radioType = NETWORK_TYPE_GPRS;
+ } else if (radioString.equals("EDGE")) {
+ radioType = NETWORK_TYPE_EDGE;
+ } else if (radioString.equals("UMTS")) {
+ radioType = NETWORK_TYPE_UMTS;
+ } else if (radioString.equals("HSDPA")) {
+ radioType = NETWORK_TYPE_HSDPA;
+ } else if (radioString.equals("HSUPA")) {
+ radioType = NETWORK_TYPE_HSUPA;
+ } else if (radioString.equals("HSPA")) {
+ radioType = NETWORK_TYPE_HSPA;
+ } else {
+ radioType = NETWORK_TYPE_UNKNOWN;
+ }
+
+ // Interpret the location based on radio access type
+ if (radioType != NETWORK_TYPE_UNKNOWN) {
+ for (int i = 0 ; i < num ; i++) {
+ rssi = p.readInt();
+ location = p.readString();
+ cell = new NeighboringCellInfo(rssi, location, radioType);
+ response.add(cell);
+ }
+ }
+ return response;
+ }
+
+ private Object responseGetPreferredNetworkType(Parcel p) {
+ int [] response = (int[]) responseInts(p);
+
+ if (response.length >= 1) {
+ // Since this is the response for getPreferredNetworkType
+ // we'll assume that it should be the value we want the
+ // vendor ril to take if we reestablish a connection to it.
+ mPreferredNetworkType = response[0];
+ }
+ return response;
+ }
+
+ private Object responseGmsBroadcastConfig(Parcel p) {
+ int num;
+ ArrayList response;
+ SmsBroadcastConfigInfo info;
+
+ num = p.readInt();
+ response = new ArrayList(num);
+
+ for (int i = 0; i < num; i++) {
+ int fromId = p.readInt();
+ int toId = p.readInt();
+ int fromScheme = p.readInt();
+ int toScheme = p.readInt();
+ boolean selected = (p.readInt() == 1);
+
+ info = new SmsBroadcastConfigInfo(fromId, toId, fromScheme,
+ toScheme, selected);
+ response.add(info);
+ }
+ return response;
+ }
+
+ private Object
+ responseCdmaBroadcastConfig(Parcel p) {
+ int numServiceCategories;
+ int response[];
+
+ numServiceCategories = p.readInt();
+
+ if (numServiceCategories == 0) {
+ // TODO: The logic of providing default values should
+ // not be done by this transport layer. And needs to
+ // be done by the vendor ril or application logic.
+ int numInts;
+ numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES * CDMA_BSI_NO_OF_INTS_STRUCT + 1;
+ response = new int[numInts];
+
+ // Faking a default record for all possible records.
+ response[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES;
+
+ // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as
+ // default language and selection status to false for all.
+ for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT ) {
+ response[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT;
+ response[i + 1] = 1;
+ response[i + 2] = 0;
+ }
+ } else {
+ int numInts;
+ numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1;
+ response = new int[numInts];
+
+ response[0] = numServiceCategories;
+ for (int i = 1 ; i < numInts; i++) {
+ response[i] = p.readInt();
+ }
+ }
+
+ return response;
+ }
+
+ private Object
+ responseSignalStrength(Parcel p) {
+ int numInts = 12;
+ int response[];
+
+ /* TODO: Add SignalStrength class to match RIL_SignalStrength */
+ response = new int[numInts];
+ for (int i = 0 ; i < numInts ; i++) {
+ response[i] = p.readInt();
+ }
+
+ return response;
+ }
+
+ private ArrayList
+ responseCdmaInformationRecord(Parcel p) {
+ int numberOfInfoRecs;
+ ArrayList response;
+
+ /**
+ * Loop through all of the information records unmarshalling them
+ * and converting them to Java Objects.
+ */
+ numberOfInfoRecs = p.readInt();
+ response = new ArrayList(numberOfInfoRecs);
+
+ for (int i = 0; i < numberOfInfoRecs; i++) {
+ CdmaInformationRecords InfoRec = new CdmaInformationRecords(p);
+ response.add(InfoRec);
+ }
+
+ return response;
+ }
+
+ private Object
+ responseCdmaCallWaiting(Parcel p) {
+ CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification();
+
+ notification.number = p.readString();
+ notification.numberPresentation = notification.presentationFromCLIP(p.readInt());
+ notification.name = p.readString();
+ notification.namePresentation = notification.numberPresentation;
+ notification.isPresent = p.readInt();
+ notification.signalType = p.readInt();
+ notification.alertPitch = p.readInt();
+ notification.signal = p.readInt();
+ notification.numberType = p.readInt();
+ notification.numberPlan = p.readInt();
+
+ return notification;
+ }
+
+ private Object
+ responseCallRing(Parcel p){
+ char response[] = new char[4];
+
+ response[0] = (char) p.readInt(); // isPresent
+ response[1] = (char) p.readInt(); // signalType
+ response[2] = (char) p.readInt(); // alertPitch
+ response[3] = (char) p.readInt(); // signal
+
+ return response;
+ }
+
+ private void
+ notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) {
+ int response = RIL_UNSOL_CDMA_INFO_REC;
+ if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) {
+ if (mDisplayInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mDisplayInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) {
+ if (mSignalInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mSignalInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) {
+ if (mNumberInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mNumberInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) {
+ if (mRedirNumInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mRedirNumInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) {
+ if (mLineControlInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mLineControlInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) {
+ if (mT53ClirInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mT53ClirInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) {
+ if (mT53AudCntrlInfoRegistrants != null) {
+ if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
+ mT53AudCntrlInfoRegistrants.notifyRegistrants(
+ new AsyncResult (null, infoRec.record, null));
+ }
+ }
+ }
+
+ static String
+ requestToString(int request) {
+/*
+ cat libs/telephony/ril_commands.h \
+ | egrep "^ *{RIL_" \
+ | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/'
+*/
+ switch(request) {
+ case RIL_REQUEST_GET_SIM_STATUS: return "GET_SIM_STATUS";
+ case RIL_REQUEST_ENTER_SIM_PIN: return "ENTER_SIM_PIN";
+ case RIL_REQUEST_ENTER_SIM_PUK: return "ENTER_SIM_PUK";
+ case RIL_REQUEST_ENTER_SIM_PIN2: return "ENTER_SIM_PIN2";
+ case RIL_REQUEST_ENTER_SIM_PUK2: return "ENTER_SIM_PUK2";
+ case RIL_REQUEST_CHANGE_SIM_PIN: return "CHANGE_SIM_PIN";
+ case RIL_REQUEST_CHANGE_SIM_PIN2: return "CHANGE_SIM_PIN2";
+ case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "ENTER_NETWORK_DEPERSONALIZATION";
+ case RIL_REQUEST_GET_CURRENT_CALLS: return "GET_CURRENT_CALLS";
+ case RIL_REQUEST_DIAL: return "DIAL";
+ case RIL_REQUEST_GET_IMSI: return "GET_IMSI";
+ case RIL_REQUEST_HANGUP: return "HANGUP";
+ case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND";
+ case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND";
+ case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE";
+ case RIL_REQUEST_CONFERENCE: return "CONFERENCE";
+ case RIL_REQUEST_UDUB: return "UDUB";
+ case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE";
+ case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH";
+ case RIL_REQUEST_VOICE_REGISTRATION_STATE: return "VOICE_REGISTRATION_STATE";
+ case RIL_REQUEST_DATA_REGISTRATION_STATE: return "DATA_REGISTRATION_STATE";
+ case RIL_REQUEST_OPERATOR: return "OPERATOR";
+ case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER";
+ case RIL_REQUEST_DTMF: return "DTMF";
+ case RIL_REQUEST_SEND_SMS: return "SEND_SMS";
+ case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "SEND_SMS_EXPECT_MORE";
+ case RIL_REQUEST_SETUP_DATA_CALL: return "SETUP_DATA_CALL";
+ case RIL_REQUEST_SIM_IO: return "SIM_IO";
+ case RIL_REQUEST_SEND_USSD: return "SEND_USSD";
+ case RIL_REQUEST_CANCEL_USSD: return "CANCEL_USSD";
+ case RIL_REQUEST_GET_CLIR: return "GET_CLIR";
+ case RIL_REQUEST_SET_CLIR: return "SET_CLIR";
+ case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "QUERY_CALL_FORWARD_STATUS";
+ case RIL_REQUEST_SET_CALL_FORWARD: return "SET_CALL_FORWARD";
+ case RIL_REQUEST_QUERY_CALL_WAITING: return "QUERY_CALL_WAITING";
+ case RIL_REQUEST_SET_CALL_WAITING: return "SET_CALL_WAITING";
+ case RIL_REQUEST_SMS_ACKNOWLEDGE: return "SMS_ACKNOWLEDGE";
+ case RIL_REQUEST_GET_IMEI: return "GET_IMEI";
+ case RIL_REQUEST_GET_IMEISV: return "GET_IMEISV";
+ case RIL_REQUEST_ANSWER: return "ANSWER";
+ case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "DEACTIVATE_DATA_CALL";
+ case RIL_REQUEST_QUERY_FACILITY_LOCK: return "QUERY_FACILITY_LOCK";
+ case RIL_REQUEST_SET_FACILITY_LOCK: return "SET_FACILITY_LOCK";
+ case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "CHANGE_BARRING_PASSWORD";
+ case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "QUERY_NETWORK_SELECTION_MODE";
+ case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "SET_NETWORK_SELECTION_AUTOMATIC";
+ case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "SET_NETWORK_SELECTION_MANUAL";
+ case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : return "QUERY_AVAILABLE_NETWORKS ";
+ case RIL_REQUEST_DTMF_START: return "DTMF_START";
+ case RIL_REQUEST_DTMF_STOP: return "DTMF_STOP";
+ case RIL_REQUEST_BASEBAND_VERSION: return "BASEBAND_VERSION";
+ case RIL_REQUEST_SEPARATE_CONNECTION: return "SEPARATE_CONNECTION";
+ case RIL_REQUEST_SET_MUTE: return "SET_MUTE";
+ case RIL_REQUEST_GET_MUTE: return "GET_MUTE";
+ case RIL_REQUEST_QUERY_CLIP: return "QUERY_CLIP";
+ case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: return "LAST_DATA_CALL_FAIL_CAUSE";
+ case RIL_REQUEST_DATA_CALL_LIST: return "DATA_CALL_LIST";
+ case RIL_REQUEST_RESET_RADIO: return "RESET_RADIO";
+ case RIL_REQUEST_OEM_HOOK_RAW: return "OEM_HOOK_RAW";
+ case RIL_REQUEST_OEM_HOOK_STRINGS: return "OEM_HOOK_STRINGS";
+ case RIL_REQUEST_SCREEN_STATE: return "SCREEN_STATE";
+ case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: return "SET_SUPP_SVC_NOTIFICATION";
+ case RIL_REQUEST_WRITE_SMS_TO_SIM: return "WRITE_SMS_TO_SIM";
+ case RIL_REQUEST_DELETE_SMS_ON_SIM: return "DELETE_SMS_ON_SIM";
+ case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE";
+ case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE";
+ case RIL_REQUEST_STK_GET_PROFILE: return "REQUEST_STK_GET_PROFILE";
+ case RIL_REQUEST_STK_SET_PROFILE: return "REQUEST_STK_SET_PROFILE";
+ case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "REQUEST_STK_SEND_ENVELOPE_COMMAND";
+ case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "REQUEST_STK_SEND_TERMINAL_RESPONSE";
+ case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
+ case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER";
+ case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "REQUEST_SET_PREFERRED_NETWORK_TYPE";
+ case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
+ case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS";
+ case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES";
+ case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
+ case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
+ case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
+ case RIL_REQUEST_SET_TTY_MODE: return "RIL_REQUEST_SET_TTY_MODE";
+ case RIL_REQUEST_QUERY_TTY_MODE: return "RIL_REQUEST_QUERY_TTY_MODE";
+ case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
+ case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
+ case RIL_REQUEST_CDMA_FLASH: return "RIL_REQUEST_CDMA_FLASH";
+ case RIL_REQUEST_CDMA_BURST_DTMF: return "RIL_REQUEST_CDMA_BURST_DTMF";
+ case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_REQUEST_CDMA_SEND_SMS";
+ case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE";
+ case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG";
+ case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG";
+ case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG";
+ case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG";
+ case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION";
+ case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY";
+ case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION";
+ case RIL_REQUEST_CDMA_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SUBSCRIPTION";
+ case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM";
+ case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM";
+ case RIL_REQUEST_DEVICE_IDENTITY: return "RIL_REQUEST_DEVICE_IDENTITY";
+ case RIL_REQUEST_GET_SMSC_ADDRESS: return "RIL_REQUEST_GET_SMSC_ADDRESS";
+ case RIL_REQUEST_SET_SMSC_ADDRESS: return "RIL_REQUEST_SET_SMSC_ADDRESS";
+ case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
+ case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
+ case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
+ case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
+ case RIL_REQUEST_ISIM_AUTHENTICATION: return "RIL_REQUEST_ISIM_AUTHENTICATION";
+ case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
+ case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
+ case RIL_REQUEST_VOICE_RADIO_TECH: return "RIL_REQUEST_VOICE_RADIO_TECH";
+ default: return "";
+ }
+ }
+
+ static String
+ responseToString(int request)
+ {
+/*
+ cat libs/telephony/ril_unsol_commands.h \
+ | egrep "^ *{RIL_" \
+ | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/'
+*/
+ switch(request) {
+ case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
+ case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
+ case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED";
+ case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS";
+ case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT";
+ case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM";
+ case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD";
+ case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST";
+ case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED";
+ case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH";
+ case RIL_UNSOL_DATA_CALL_LIST_CHANGED: return "UNSOL_DATA_CALL_LIST_CHANGED";
+ case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION";
+ case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END";
+ case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND";
+ case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY";
+ case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP";
+ case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL";
+ case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH";
+ case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING";
+ case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED";
+ case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_RESPONSE_CDMA_NEW_SMS";
+ case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_RESPONSE_NEW_BROADCAST_SMS";
+ case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL";
+ case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED";
+ case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE";
+ case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING";
+ case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS";
+ case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC";
+ case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW";
+ case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONG";
+ case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE";
+ case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
+ case RIL_UNSOl_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED";
+ case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
+ case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
+ case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: return "UNSOL_VOICE_RADIO_TECH_CHANGED";
+ default: return "";
+ }
+ }
+
+ private void riljLog(String msg) {
+ Log.d(LOG_TAG, msg);
+ }
+
+ private void riljLogv(String msg) {
+ Log.v(LOG_TAG, msg);
+ }
+
+ private void unsljLog(int response) {
+ riljLog("[UNSL]< " + responseToString(response));
+ }
+
+ private void unsljLogMore(int response, String more) {
+ riljLog("[UNSL]< " + responseToString(response) + " " + more);
+ }
+
+ private void unsljLogRet(int response, Object ret) {
+ riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
+ }
+
+ private void unsljLogvRet(int response, Object ret) {
+ riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
+ }
+
+
+ // ***** Methods for CDMA support
+ public void
+ getDeviceIdentity(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DEVICE_IDENTITY, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void
+ getCDMASubscription(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SUBSCRIPTION, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ @Override
+ public void setPhoneType(int phoneType) { // Called by CDMAPhone and GSMPhone constructor
+ if (RILJ_LOGD) riljLog("setPhoneType=" + phoneType + " old value=" + mPhoneType);
+ mPhoneType = phoneType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void queryCdmaRoamingPreference(Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(cdmaRoamingType);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + cdmaRoamingType);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCdmaSubscriptionSource(int cdmaSubscription , Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(cdmaSubscription);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + cdmaSubscription);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getCdmaSubscriptionSource(Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void queryTTYMode(Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setTTYMode(int ttyMode, Message response) {
+ RILRequest rr = RILRequest.obtain(
+ RILConstants.RIL_REQUEST_SET_TTY_MODE, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(ttyMode);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + ttyMode);
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void
+ sendCDMAFeatureCode(String FeatureCode, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_FLASH, response);
+
+ rr.mp.writeString(FeatureCode);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " : " + FeatureCode);
+
+ send(rr);
+ }
+
+ public void getCdmaBroadcastConfig(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, response);
+
+ send(rr);
+ }
+
+ // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig
+ public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, response);
+
+ for(int i = 0; i < configValuesArray.length; i++) {
+ rr.mp.writeInt(configValuesArray[i]);
+ }
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void setCdmaBroadcastActivation(boolean activate, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, response);
+
+ rr.mp.writeInt(1);
+ rr.mp.writeInt(activate ? 0 :1);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void exitEmergencyCallbackMode(Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, response);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ public void requestIsimAuthentication(String nonce, Message response) {
+ RILRequest rr = RILRequest.obtain(RIL_REQUEST_ISIM_AUTHENTICATION, response);
+
+ rr.mp.writeString(nonce);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ send(rr);
+ }
+
+ /* (non-Javadoc)
+ * @see com.android.internal.telephony.BaseCommands#testingEmergencyCall()
+ */
+ @Override
+ public void testingEmergencyCall() {
+ if (RILJ_LOGD) riljLog("testingEmergencyCall");
+ mTestingEmergencyCall.set(true);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("RIL:");
+ pw.println(" mSocket=" + mSocket);
+ pw.println(" mSenderThread=" + mSenderThread);
+ pw.println(" mSender=" + mSender);
+ pw.println(" mReceiverThread=" + mReceiverThread);
+ pw.println(" mReceiver=" + mReceiver);
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
+ synchronized (mRequestsList) {
+ pw.println(" mRequestMessagesPending=" + mRequestMessagesPending);
+ pw.println(" mRequestMessagesWaiting=" + mRequestMessagesWaiting);
+ int count = mRequestsList.size();
+ pw.println(" mRequestList count=" + count);
+ for (int i = 0; i < count; i++) {
+ RILRequest rr = mRequestsList.get(i);
+ pw.println(" [" + rr.mSerial + "] " + requestToString(rr.mRequest));
+ }
+ }
+ pw.println(" mLastNITZTimeInfo=" + mLastNITZTimeInfo);
+ pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RestrictedState.java b/src/java/com/android/internal/telephony/RestrictedState.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad2b88d584da83d4a1d5830c3441b79163b8a83b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RestrictedState.java
@@ -0,0 +1,122 @@
+/*
+ * 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.telephony.ServiceState;
+
+public class RestrictedState {
+
+ /**
+ * Set true to block packet data access due to restriction
+ */
+ private boolean mPsRestricted;
+ /**
+ * Set true to block all normal voice/SMS/USSD/SS/AV64 due to restriction
+ */
+ private boolean mCsNormalRestricted;
+ /**
+ * Set true to block emergency call due to restriction
+ */
+ private boolean mCsEmergencyRestricted;
+
+ public RestrictedState() {
+ setPsRestricted(false);
+ setCsNormalRestricted(false);
+ setCsEmergencyRestricted(false);
+ }
+
+ /**
+ * @param csEmergencyRestricted the csEmergencyRestricted to set
+ */
+ public void setCsEmergencyRestricted(boolean csEmergencyRestricted) {
+ mCsEmergencyRestricted = csEmergencyRestricted;
+ }
+
+ /**
+ * @return the csEmergencyRestricted
+ */
+ public boolean isCsEmergencyRestricted() {
+ return mCsEmergencyRestricted;
+ }
+
+ /**
+ * @param csNormalRestricted the csNormalRestricted to set
+ */
+ public void setCsNormalRestricted(boolean csNormalRestricted) {
+ mCsNormalRestricted = csNormalRestricted;
+ }
+
+ /**
+ * @return the csNormalRestricted
+ */
+ public boolean isCsNormalRestricted() {
+ return mCsNormalRestricted;
+ }
+
+ /**
+ * @param psRestricted the psRestricted to set
+ */
+ public void setPsRestricted(boolean psRestricted) {
+ mPsRestricted = psRestricted;
+ }
+
+ /**
+ * @return the psRestricted
+ */
+ public boolean isPsRestricted() {
+ return mPsRestricted;
+ }
+
+ public boolean isCsRestricted() {
+ return mCsNormalRestricted && mCsEmergencyRestricted;
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ RestrictedState s;
+
+ try {
+ s = (RestrictedState) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mPsRestricted == s.mPsRestricted
+ && mCsNormalRestricted == s.mCsNormalRestricted
+ && mCsEmergencyRestricted == s.mCsEmergencyRestricted;
+ }
+
+ @Override
+ public String toString() {
+ String csString = "none";
+
+ if (mCsEmergencyRestricted && mCsNormalRestricted) {
+ csString = "all";
+ } else if (mCsEmergencyRestricted && !mCsNormalRestricted) {
+ csString = "emergency";
+ } else if (!mCsEmergencyRestricted && mCsNormalRestricted) {
+ csString = "normal call";
+ }
+
+ return "Restricted State CS: " + csString + " PS:" + mPsRestricted;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..250d99eaa623b2af3d414dd3f73b8e70bce0020d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -0,0 +1,410 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+import android.util.Pair;
+import android.text.TextUtils;
+
+import java.util.Random;
+import java.util.ArrayList;
+
+/**
+ * Retry manager allows a simple way to declare a series of
+ * retry timeouts. After creating a RetryManager the configure
+ * method is used to define the sequence. A simple linear series
+ * may be initialized using configure with three integer parameters
+ * The other configure method allows a series to be declared using
+ * a string.
+ *
+ * The format of the configuration string is a series of parameters
+ * separated by a comma. There are two name value pair parameters plus a series
+ * of delay times. The units of of these delay times is unspecified.
+ * The name value pairs which may be specified are:
+ *
+ *- max_retries=
+ *
- default_randomizationTime=
+ *
+ *
+ * max_retries is the number of times that incrementRetryCount
+ * maybe called before isRetryNeeded will return false. if value
+ * is infinite then isRetryNeeded will always return true.
+ *
+ * default_randomizationTime will be used as the randomizationTime
+ * for delay times which have no supplied randomizationTime. If
+ * default_randomizationTime is not defined it defaults to 0.
+ *
+ * The other parameters define The series of delay times and each
+ * may have an optional randomization value separated from the
+ * delay time by a colon.
+ *
+ * Examples:
+ *
+ * - 3 retries with no randomization value which means its 0:
+ *
+ *
+ *
- 10 retries with a 500 default randomization value for each and
+ * the 4..10 retries all using 3000 as the delay:
+ *
"max_retries=10, default_randomization=500, 1000, 2000, 3000"
+ *
+ * - 4 retries with a 100 as the default randomization value for the first 2 values and
+ * the other two having specified values of 500:
+ *
"default_randomization=100, 1000, 2000, 4000:500, 5000:500"
+ *
+ * - Infinite number of retries with the first one at 1000, the second at 2000 all
+ * others will be at 3000.
+ *
"max_retries=infinite,1000,2000,3000
+ *
+ *
+ * {@hide}
+ */
+public class RetryManager {
+ static public final String LOG_TAG = "GSM";
+ static public final boolean DBG = true;
+ static public final boolean VDBG = false;
+
+ /**
+ * Retry record with times in milli-seconds
+ */
+ private static class RetryRec {
+ RetryRec(int delayTime, int randomizationTime) {
+ mDelayTime = delayTime;
+ mRandomizationTime = randomizationTime;
+ }
+
+ int mDelayTime;
+ int mRandomizationTime;
+ }
+
+ /** The array of retry records */
+ private ArrayList mRetryArray = new ArrayList();
+
+ /** When true isRetryNeeded() will always return true */
+ private boolean mRetryForever;
+
+ /**
+ * The maximum number of retries to attempt before
+ * isRetryNeeded returns false
+ */
+ private int mMaxRetryCount;
+
+ /** The current number of retries */
+ private int mRetryCount;
+
+ /** Random number generator */
+ private Random rng = new Random();
+
+ private String mConfig;
+
+ /** Constructor */
+ public RetryManager() {
+ if (VDBG) log("constructor");
+ }
+
+ public String toString() {
+ String ret = "RetryManager: forever=" + mRetryForever + ", maxRetry=" + mMaxRetryCount +
+ ", retry=" + mRetryCount + ",\n " + mConfig;
+ for (RetryRec r : mRetryArray) {
+ ret += "\n " + r.mDelayTime + ":" + r.mRandomizationTime;
+ }
+ return ret;
+ }
+
+ /**
+ * Configure for a simple linear sequence of times plus
+ * a random value.
+ *
+ * @param maxRetryCount is the maximum number of retries
+ * before isRetryNeeded returns false.
+ * @param retryTime is a time that will be returned by getRetryTime.
+ * @param randomizationTime a random value between 0 and
+ * randomizationTime will be added to retryTime. this
+ * parameter may be 0.
+ * @return true if successful
+ */
+ public boolean configure(int maxRetryCount, int retryTime, int randomizationTime) {
+ Pair value;
+
+ if (VDBG) log("configure: " + maxRetryCount + ", " + retryTime + "," + randomizationTime);
+
+ if (!validateNonNegativeInt("maxRetryCount", maxRetryCount)) {
+ return false;
+ }
+
+ if (!validateNonNegativeInt("retryTime", retryTime)) {
+ return false;
+ }
+
+ if (!validateNonNegativeInt("randomizationTime", randomizationTime)) {
+ return false;
+ }
+
+ mMaxRetryCount = maxRetryCount;
+ resetRetryCount();
+ mRetryArray.clear();
+ mRetryArray.add(new RetryRec(retryTime, randomizationTime));
+
+ return true;
+ }
+
+ /**
+ * Configure for using string which allow arbitrary
+ * sequences of times. See class comments for the
+ * string format.
+ *
+ * @return true if successful
+ */
+ public boolean configure(String configStr) {
+ // Strip quotes if present.
+ if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
+ configStr = configStr.substring(1, configStr.length()-1);
+ }
+ if (VDBG) log("configure: '" + configStr + "'");
+ mConfig = configStr;
+
+ if (!TextUtils.isEmpty(configStr)) {
+ int defaultRandomization = 0;
+
+ if (VDBG) log("configure: not empty");
+
+ mMaxRetryCount = 0;
+ resetRetryCount();
+ mRetryArray.clear();
+
+ String strArray[] = configStr.split(",");
+ for (int i = 0; i < strArray.length; i++) {
+ if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
+ Pair value;
+ String splitStr[] = strArray[i].split("=", 2);
+ splitStr[0] = splitStr[0].trim();
+ if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
+ if (splitStr.length > 1) {
+ splitStr[1] = splitStr[1].trim();
+ if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+ if (TextUtils.equals(splitStr[0], "default_randomization")) {
+ value = parseNonNegativeInt(splitStr[0], splitStr[1]);
+ if (!value.first) return false;
+ defaultRandomization = value.second;
+ } else if (TextUtils.equals(splitStr[0], "max_retries")) {
+ if (TextUtils.equals("infinite",splitStr[1])) {
+ mRetryForever = true;
+ } else {
+ value = parseNonNegativeInt(splitStr[0], splitStr[1]);
+ if (!value.first) return false;
+ mMaxRetryCount = value.second;
+ }
+ } else {
+ Log.e(LOG_TAG, "Unrecognized configuration name value pair: "
+ + strArray[i]);
+ return false;
+ }
+ } else {
+ /**
+ * Assume a retry time with an optional randomization value
+ * following a ":"
+ */
+ splitStr = strArray[i].split(":", 2);
+ splitStr[0] = splitStr[0].trim();
+ RetryRec rr = new RetryRec(0, 0);
+ value = parseNonNegativeInt("delayTime", splitStr[0]);
+ if (!value.first) return false;
+ rr.mDelayTime = value.second;
+
+ // Check if optional randomization value present
+ if (splitStr.length > 1) {
+ splitStr[1] = splitStr[1].trim();
+ if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+ value = parseNonNegativeInt("randomizationTime", splitStr[1]);
+ if (!value.first) return false;
+ rr.mRandomizationTime = value.second;
+ } else {
+ rr.mRandomizationTime = defaultRandomization;
+ }
+ mRetryArray.add(rr);
+ }
+ }
+ if (mRetryArray.size() > mMaxRetryCount) {
+ mMaxRetryCount = mRetryArray.size();
+ if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
+ }
+ if (VDBG) log("configure: true");
+ return true;
+ } else {
+ if (VDBG) log("configure: false it's empty");
+ return false;
+ }
+ }
+
+ /**
+ * Report whether data reconnection should be retried
+ *
+ * @return {@code true} if the max retries has not been reached. {@code
+ * false} otherwise.
+ */
+ public boolean isRetryNeeded() {
+ boolean retVal = mRetryForever || (mRetryCount < mMaxRetryCount);
+ if (DBG) log("isRetryNeeded: " + retVal);
+ return retVal;
+ }
+
+ /**
+ * Return the timer that should be used to trigger the data reconnection
+ */
+ public int getRetryTimer() {
+ int index;
+ if (mRetryCount < mRetryArray.size()) {
+ index = mRetryCount;
+ } else {
+ index = mRetryArray.size() - 1;
+ }
+
+ int retVal;
+ if ((index >= 0) && (index < mRetryArray.size())) {
+ retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
+ } else {
+ retVal = 0;
+ }
+
+ if (DBG) log("getRetryTimer: " + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return retry count
+ */
+ public int getRetryCount() {
+ if (DBG) log("getRetryCount: " + mRetryCount);
+ return mRetryCount;
+ }
+
+ /**
+ * Increase the retry counter, does not change retry forever.
+ */
+ public void increaseRetryCount() {
+ mRetryCount++;
+ if (mRetryCount > mMaxRetryCount) {
+ mRetryCount = mMaxRetryCount;
+ }
+ if (DBG) log("increaseRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Set retry count to the specified value
+ */
+ public void setRetryCount(int count) {
+ mRetryCount = count;
+ if (mRetryCount > mMaxRetryCount) {
+ mRetryCount = mMaxRetryCount;
+ }
+
+ if (mRetryCount < 0) {
+ mRetryCount = 0;
+ }
+
+ if (DBG) log("setRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Set retry forever to the specified value
+ */
+ public void setRetryForever(boolean retryForever) {
+ mRetryForever = retryForever;
+ if (DBG) log("setRetryForever: " + mRetryForever);
+ }
+
+ /**
+ * Clear the data-retry counter
+ */
+ public void resetRetryCount() {
+ mRetryCount = 0;
+ if (DBG) log("resetRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Retry forever using last timeout time.
+ */
+ public void retryForeverUsingLastTimeout() {
+ mRetryCount = mMaxRetryCount;
+ mRetryForever = true;
+ if (DBG) log("retryForeverUsingLastTimeout: " + mRetryForever + ", " + mRetryCount);
+ }
+
+ /**
+ * @return true if retrying forever
+ */
+ public boolean isRetryForever() {
+ if (DBG) log("isRetryForever: " + mRetryForever);
+ return mRetryForever;
+ }
+
+ /**
+ * Parse an integer validating the value is not negative.
+ *
+ * @param name
+ * @param stringValue
+ * @return Pair.first == true if stringValue an integer >= 0
+ */
+ private Pair parseNonNegativeInt(String name, String stringValue) {
+ int value;
+ Pair retVal;
+ try {
+ value = Integer.parseInt(stringValue);
+ retVal = new Pair(validateNonNegativeInt(name, value), value);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, name + " bad value: " + stringValue, e);
+ retVal = new Pair(false, 0);
+ }
+ if (VDBG) log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
+ + retVal.first + ", " + retVal.second);
+ return retVal;
+ }
+
+ /**
+ * Validate an integer is >= 0 and logs an error if not
+ *
+ * @param name
+ * @param value
+ * @return Pair.first
+ */
+ private boolean validateNonNegativeInt(String name, int value) {
+ boolean retVal;
+ if (value < 0) {
+ Log.e(LOG_TAG, name + " bad value: is < 0");
+ retVal = false;
+ } else {
+ retVal = true;
+ }
+ if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
+ return retVal;
+ }
+
+ /**
+ * Return next random number for the index
+ */
+ private int nextRandomizationTime(int index) {
+ int randomTime = mRetryArray.get(index).mRandomizationTime;
+ if (randomTime == 0) {
+ return 0;
+ } else {
+ return rng.nextInt(randomTime);
+ }
+ }
+
+ private void log(String s) {
+ Log.d(LOG_TAG, "[RM] " + s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a6c5dc62d91ac2da70c8e99c16bdf0f3828f942
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -0,0 +1,1252 @@
+/*
+ * 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.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.provider.Telephony;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.SmsCbMessage;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.Spanned;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Random;
+
+import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
+import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
+import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
+import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
+import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
+
+public abstract class SMSDispatcher extends Handler {
+ static final String TAG = "SMS"; // accessed from inner class
+ private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
+
+ /** Permission required to receive SMS and SMS-CB messages. */
+ public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS";
+
+ /** Permission required to receive ETWS and CMAS emergency broadcasts. */
+ public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION =
+ "android.permission.RECEIVE_EMERGENCY_BROADCAST";
+
+ /** Permission required to send SMS to short codes without user confirmation. */
+ private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION =
+ "android.permission.SEND_SMS_NO_CONFIRMATION";
+
+ /** Query projection for checking for duplicate message segments. */
+ private static final String[] PDU_PROJECTION = new String[] {
+ "pdu"
+ };
+
+ /** Query projection for combining concatenated message segments. */
+ private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] {
+ "pdu",
+ "sequence",
+ "destination_port"
+ };
+
+ private static final int PDU_COLUMN = 0;
+ private static final int SEQUENCE_COLUMN = 1;
+ private static final int DESTINATION_PORT_COLUMN = 2;
+
+ /** New SMS received. */
+ protected static final int EVENT_NEW_SMS = 1;
+
+ /** SMS send complete. */
+ protected static final int EVENT_SEND_SMS_COMPLETE = 2;
+
+ /** Retry sending a previously failed SMS message */
+ private static final int EVENT_SEND_RETRY = 3;
+
+ /** Confirmation required for sending a large number of messages. */
+ private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
+
+ /** Send the user confirmed SMS */
+ static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class
+
+ /** Don't send SMS (user did not confirm). */
+ static final int EVENT_STOP_SENDING = 7; // accessed from inner class
+
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
+
+ /** Confirmation required for third-party apps sending to an SMS short code. */
+ private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
+
+ protected final Phone mPhone;
+ protected final Context mContext;
+ protected final ContentResolver mResolver;
+ protected final CommandsInterface mCm;
+ protected final SmsStorageMonitor mStorageMonitor;
+ protected final TelephonyManager mTelephonyManager;
+
+ protected final WapPushOverSms mWapPush;
+
+ protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
+
+ /** Maximum number of times to retry sending a failed SMS. */
+ private static final int MAX_SEND_RETRIES = 3;
+ /** Delay before next send attempt on a failed SMS, in milliseconds. */
+ private static final int SEND_RETRY_DELAY = 2000;
+ /** single part SMS */
+ private static final int SINGLE_PART_SMS = 1;
+ /** Message sending queue limit */
+ private static final int MO_MSG_QUEUE_LIMIT = 5;
+
+ /**
+ * Message reference for a CONCATENATED_8_BIT_REFERENCE or
+ * CONCATENATED_16_BIT_REFERENCE message set. Should be
+ * incremented for each set of concatenated messages.
+ * Static field shared by all dispatcher objects.
+ */
+ private static int sConcatenatedRef = new Random().nextInt(256);
+
+ /** Outgoing message counter. Shared by all dispatchers. */
+ private final SmsUsageMonitor mUsageMonitor;
+
+ /** Number of outgoing SmsTrackers waiting for user confirmation. */
+ private int mPendingTrackerCount;
+
+ /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
+ private PowerManager.WakeLock mWakeLock;
+
+ /**
+ * Hold the wake lock for 5 seconds, which should be enough time for
+ * any receiver(s) to grab its own wake lock.
+ */
+ private static final int WAKE_LOCK_TIMEOUT = 5000;
+
+ /* Flags indicating whether the current device allows sms service */
+ protected boolean mSmsCapable = true;
+ protected boolean mSmsReceiveDisabled;
+ protected boolean mSmsSendDisabled;
+
+ protected int mRemainingMessages = -1;
+
+ protected static int getNextConcatenatedRef() {
+ sConcatenatedRef += 1;
+ return sConcatenatedRef;
+ }
+
+ /**
+ * Create a new SMS dispatcher.
+ * @param phone the Phone to use
+ * @param storageMonitor the SmsStorageMonitor to use
+ * @param usageMonitor the SmsUsageMonitor to use
+ */
+ protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+ SmsUsageMonitor usageMonitor) {
+ mPhone = phone;
+ mWapPush = new WapPushOverSms(phone, this);
+ mContext = phone.getContext();
+ mResolver = mContext.getContentResolver();
+ mCm = phone.mCM;
+ mStorageMonitor = storageMonitor;
+ mUsageMonitor = usageMonitor;
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+
+ createWakelock();
+
+ mSmsCapable = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_sms_capable);
+ mSmsReceiveDisabled = !SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable);
+ mSmsSendDisabled = !SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable);
+ Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
+ + " mSmsReceiveDisabled=" + mSmsReceiveDisabled
+ + " mSmsSendDisabled=" + mSmsSendDisabled);
+ }
+
+ /** Unregister for incoming SMS events. */
+ public abstract void dispose();
+
+ /**
+ * The format of the message PDU in the associated broadcast intent.
+ * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+ * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+ *
+ * Note: All applications which handle incoming SMS messages by processing the
+ * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
+ * into the new methods in {@link android.telephony.SmsMessage} which take an
+ * extra format parameter. This is required in order to correctly decode the PDU on
+ * devices which require support for both 3GPP and 3GPP2 formats at the same time,
+ * such as CDMA/LTE devices and GSM/CDMA world phones.
+ *
+ * @return the format of the message PDU
+ */
+ protected abstract String getFormat();
+
+ @Override
+ protected void finalize() {
+ Log.d(TAG, "SMSDispatcher finalized");
+ }
+
+
+ /* TODO: Need to figure out how to keep track of status report routing in a
+ * persistent manner. If the phone process restarts (reboot or crash),
+ * we will lose this list and any status reports that come in after
+ * will be dropped.
+ */
+ /** Sent messages awaiting a delivery status report. */
+ protected final ArrayList deliveryPendingList = new ArrayList();
+
+ /**
+ * Handles events coming from the phone stack. Overridden from handler.
+ *
+ * @param msg the message to handle
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case EVENT_NEW_SMS:
+ // A new SMS has been received by the device
+ if (false) {
+ Log.d(TAG, "New SMS Message Received");
+ }
+
+ SmsMessage sms;
+
+ ar = (AsyncResult) msg.obj;
+
+ if (ar.exception != null) {
+ Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
+ return;
+ }
+
+ sms = (SmsMessage) ar.result;
+ try {
+ int result = dispatchMessage(sms.mWrappedSmsMessage);
+ if (result != Activity.RESULT_OK) {
+ // RESULT_OK means that message was broadcast for app(s) to handle.
+ // Any other result, we should ack here.
+ boolean handled = (result == Intents.RESULT_SMS_HANDLED);
+ notifyAndAcknowledgeLastIncomingSms(handled, result, null);
+ }
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "Exception dispatching message", ex);
+ notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
+ }
+
+ break;
+
+ case EVENT_SEND_SMS_COMPLETE:
+ // An outbound SMS has been successfully transferred, or failed.
+ handleSendComplete((AsyncResult) msg.obj);
+ break;
+
+ case EVENT_SEND_RETRY:
+ sendSms((SmsTracker) msg.obj);
+ break;
+
+ case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
+ handleReachSentLimit((SmsTracker)(msg.obj));
+ break;
+
+ case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(false, (SmsTracker)(msg.obj));
+ break;
+
+ case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
+ handleConfirmShortCode(true, (SmsTracker)(msg.obj));
+ break;
+
+ case EVENT_SEND_CONFIRMED_SMS:
+ {
+ SmsTracker tracker = (SmsTracker) msg.obj;
+ if (tracker.isMultipart()) {
+ sendMultipartSms(tracker);
+ } else {
+ sendSms(tracker);
+ }
+ mPendingTrackerCount--;
+ break;
+ }
+
+ case EVENT_STOP_SENDING:
+ {
+ SmsTracker tracker = (SmsTracker) msg.obj;
+ if (tracker.mSentIntent != null) {
+ try {
+ tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
+ } catch (CanceledException ex) {
+ Log.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED");
+ }
+ }
+ mPendingTrackerCount--;
+ break;
+ }
+ }
+ }
+
+ private void createWakelock() {
+ PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher");
+ mWakeLock.setReferenceCounted(true);
+ }
+
+ /**
+ * Grabs a wake lock and sends intent as an ordered broadcast.
+ * The resultReceiver will check for errors and ACK/NACK back
+ * to the RIL.
+ *
+ * @param intent intent to broadcast
+ * @param permission Receivers are required to have this permission
+ */
+ public void dispatch(Intent intent, String permission) {
+ // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any
+ // receivers time to take their own wake locks.
+ mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
+ mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,
+ this, Activity.RESULT_OK, null, null);
+ }
+
+ /**
+ * Called when SMS send completes. Broadcasts a sentIntent on success.
+ * On failure, either sets up retries or broadcasts a sentIntent with
+ * the failure in the result code.
+ *
+ * @param ar AsyncResult passed into the message handler. ar.result should
+ * an SmsResponse instance if send was successful. ar.userObj
+ * should be an SmsTracker instance.
+ */
+ protected void handleSendComplete(AsyncResult ar) {
+ SmsTracker tracker = (SmsTracker) ar.userObj;
+ PendingIntent sentIntent = tracker.mSentIntent;
+
+ if (ar.exception == null) {
+ if (false) {
+ Log.d(TAG, "SMS send complete. Broadcasting "
+ + "intent: " + sentIntent);
+ }
+
+ if (tracker.mDeliveryIntent != null) {
+ // Expecting a status report. Add it to the list.
+ int messageRef = ((SmsResponse)ar.result).messageRef;
+ tracker.mMessageRef = messageRef;
+ deliveryPendingList.add(tracker);
+ }
+
+ if (sentIntent != null) {
+ try {
+ if (mRemainingMessages > -1) {
+ mRemainingMessages--;
+ }
+
+ if (mRemainingMessages == 0) {
+ Intent sendNext = new Intent();
+ sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
+ sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
+ } else {
+ sentIntent.send(Activity.RESULT_OK);
+ }
+ } catch (CanceledException ex) {}
+ }
+ } else {
+ if (false) {
+ Log.d(TAG, "SMS send failed");
+ }
+
+ int ss = mPhone.getServiceState().getState();
+
+ if (ss != ServiceState.STATE_IN_SERVICE) {
+ handleNotInService(ss, tracker.mSentIntent);
+ } else if ((((CommandException)(ar.exception)).getCommandError()
+ == CommandException.Error.SMS_FAIL_RETRY) &&
+ tracker.mRetryCount < MAX_SEND_RETRIES) {
+ // Retry after a delay if needed.
+ // TODO: According to TS 23.040, 9.2.3.6, we should resend
+ // with the same TP-MR as the failed message, and
+ // TP-RD set to 1. However, we don't have a means of
+ // knowing the MR for the failed message (EF_SMSstatus
+ // may or may not have the MR corresponding to this
+ // message, depending on the failure). Also, in some
+ // implementations this retry is handled by the baseband.
+ tracker.mRetryCount++;
+ Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
+ sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
+ } else if (tracker.mSentIntent != null) {
+ int error = RESULT_ERROR_GENERIC_FAILURE;
+
+ if (((CommandException)(ar.exception)).getCommandError()
+ == CommandException.Error.FDN_CHECK_FAILURE) {
+ error = RESULT_ERROR_FDN_CHECK_FAILURE;
+ }
+ // Done retrying; return an error to the app.
+ try {
+ Intent fillIn = new Intent();
+ if (ar.result != null) {
+ fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
+ }
+ if (mRemainingMessages > -1) {
+ mRemainingMessages--;
+ }
+
+ if (mRemainingMessages == 0) {
+ fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
+ }
+
+ tracker.mSentIntent.send(mContext, error, fillIn);
+ } catch (CanceledException ex) {}
+ }
+ }
+ }
+
+ /**
+ * Handles outbound message when the phone is not in service.
+ *
+ * @param ss Current service state. Valid values are:
+ * OUT_OF_SERVICE
+ * EMERGENCY_ONLY
+ * POWER_OFF
+ * @param sentIntent the PendingIntent to send the error to
+ */
+ protected static void handleNotInService(int ss, PendingIntent sentIntent) {
+ if (sentIntent != null) {
+ try {
+ if (ss == ServiceState.STATE_POWER_OFF) {
+ sentIntent.send(RESULT_ERROR_RADIO_OFF);
+ } else {
+ sentIntent.send(RESULT_ERROR_NO_SERVICE);
+ }
+ } catch (CanceledException ex) {}
+ }
+ }
+
+ /**
+ * Dispatches an incoming SMS messages.
+ *
+ * @param sms the incoming message from the phone
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
+ */
+ public abstract int dispatchMessage(SmsMessageBase sms);
+
+ /**
+ * Dispatch a normal incoming SMS. This is called from the format-specific
+ * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required.
+ *
+ * @param sms
+ * @return
+ */
+ protected int dispatchNormalMessage(SmsMessageBase sms) {
+ SmsHeader smsHeader = sms.getUserDataHeader();
+
+ // See if message is partial or port addressed.
+ if ((smsHeader == null) || (smsHeader.concatRef == null)) {
+ // Message is not partial (not part of concatenated sequence).
+ byte[][] pdus = new byte[1][];
+ pdus[0] = sms.getPdu();
+
+ if (smsHeader != null && smsHeader.portAddrs != null) {
+ if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
+ // GSM-style WAP indication
+ return mWapPush.dispatchWapPdu(sms.getUserData());
+ } else {
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
+ }
+ } else {
+ // Normal short and non-port-addressed message, dispatch it.
+ dispatchPdus(pdus);
+ }
+ return Activity.RESULT_OK;
+ } else {
+ // Process the message part.
+ SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
+ SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
+ return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(),
+ concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount,
+ sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false);
+ }
+ }
+
+ /**
+ * If this is the last part send the parts out to the application, otherwise
+ * the part is stored for later processing. Handles both 3GPP concatenated messages
+ * as well as 3GPP2 format WAP push messages processed by
+ * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}.
+ *
+ * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment
+ * @param address the originating address
+ * @param referenceNumber distinguishes concatenated messages from the same sender
+ * @param sequenceNumber the order of this segment in the message
+ * (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages).
+ * @param messageCount the number of segments in the message
+ * @param timestamp the service center timestamp in millis
+ * @param destPort the destination port for the message, or -1 for no destination port
+ * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU
+ *
+ * @return a result code from {@link Telephony.Sms.Intents}, or
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
+ */
+ protected int processMessagePart(byte[] pdu, String address, int referenceNumber,
+ int sequenceNumber, int messageCount, long timestamp, int destPort,
+ boolean isCdmaWapPush) {
+ byte[][] pdus = null;
+ Cursor cursor = null;
+ try {
+ // used by several query selection arguments
+ String refNumber = Integer.toString(referenceNumber);
+ String seqNumber = Integer.toString(sequenceNumber);
+
+ // Check for duplicate message segment
+ cursor = mResolver.query(mRawUri, PDU_PROJECTION,
+ "address=? AND reference_number=? AND sequence=?",
+ new String[] {address, refNumber, seqNumber}, null);
+
+ // moveToNext() returns false if no duplicates were found
+ if (cursor.moveToNext()) {
+ Log.w(TAG, "Discarding duplicate message segment from address=" + address
+ + " refNumber=" + refNumber + " seqNumber=" + seqNumber);
+ String oldPduString = cursor.getString(PDU_COLUMN);
+ byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
+ if (!Arrays.equals(oldPdu, pdu)) {
+ Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length
+ + " is different from existing PDU of length " + oldPdu.length);
+ }
+ return Intents.RESULT_SMS_HANDLED;
+ }
+ cursor.close();
+
+ // not a dup, query for all other segments of this concatenated message
+ String where = "address=? AND reference_number=?";
+ String[] whereArgs = new String[] {address, refNumber};
+ cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null);
+
+ int cursorCount = cursor.getCount();
+ if (cursorCount != messageCount - 1) {
+ // We don't have all the parts yet, store this one away
+ ContentValues values = new ContentValues();
+ values.put("date", timestamp);
+ values.put("pdu", HexDump.toHexString(pdu));
+ values.put("address", address);
+ values.put("reference_number", referenceNumber);
+ values.put("count", messageCount);
+ values.put("sequence", sequenceNumber);
+ if (destPort != -1) {
+ values.put("destination_port", destPort);
+ }
+ mResolver.insert(mRawUri, values);
+ return Intents.RESULT_SMS_HANDLED;
+ }
+
+ // All the parts are in place, deal with them
+ pdus = new byte[messageCount][];
+ for (int i = 0; i < cursorCount; i++) {
+ cursor.moveToNext();
+ int cursorSequence = cursor.getInt(SEQUENCE_COLUMN);
+ // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
+ if (!isCdmaWapPush) {
+ cursorSequence--;
+ }
+ pdus[cursorSequence] = HexDump.hexStringToByteArray(
+ cursor.getString(PDU_COLUMN));
+
+ // Read the destination port from the first segment (needed for CDMA WAP PDU).
+ // It's not a bad idea to prefer the port from the first segment for 3GPP as well.
+ if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
+ destPort = cursor.getInt(DESTINATION_PORT_COLUMN);
+ }
+ }
+ // This one isn't in the DB, so add it
+ // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
+ if (isCdmaWapPush) {
+ pdus[sequenceNumber] = pdu;
+ } else {
+ pdus[sequenceNumber - 1] = pdu;
+ }
+
+ // Remove the parts from the database
+ mResolver.delete(mRawUri, where, whereArgs);
+ } catch (SQLException e) {
+ Log.e(TAG, "Can't access multipart SMS database", e);
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+
+ // Special handling for CDMA WDP datagrams
+ if (isCdmaWapPush) {
+ // Build up the data stream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ for (int i = 0; i < messageCount; i++) {
+ // reassemble the (WSP-)pdu
+ output.write(pdus[i], 0, pdus[i].length);
+ }
+ byte[] datagram = output.toByteArray();
+
+ // Dispatch the PDU to applications
+ if (destPort == SmsHeader.PORT_WAP_PUSH) {
+ // Handle the PUSH
+ return mWapPush.dispatchWapPdu(datagram);
+ } else {
+ pdus = new byte[1][];
+ pdus[0] = datagram;
+ // The messages were sent to any other WAP port
+ dispatchPortAddressedPdus(pdus, destPort);
+ return Activity.RESULT_OK;
+ }
+ }
+
+ // Dispatch the PDUs to applications
+ if (destPort != -1) {
+ if (destPort == SmsHeader.PORT_WAP_PUSH) {
+ // Build up the data stream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ for (int i = 0; i < messageCount; i++) {
+ SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat());
+ byte[] data = msg.getUserData();
+ output.write(data, 0, data.length);
+ }
+ // Handle the PUSH
+ return mWapPush.dispatchWapPdu(output.toByteArray());
+ } else {
+ // The messages were sent to a port, so concoct a URI for it
+ dispatchPortAddressedPdus(pdus, destPort);
+ }
+ } else {
+ // The messages were not sent to a port
+ dispatchPdus(pdus);
+ }
+ return Activity.RESULT_OK;
+ }
+
+ /**
+ * Dispatches standard PDUs to interested applications
+ *
+ * @param pdus The raw PDUs making up the message
+ */
+ protected void dispatchPdus(byte[][] pdus) {
+ Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
+ intent.putExtra("pdus", pdus);
+ intent.putExtra("format", getFormat());
+ dispatch(intent, RECEIVE_SMS_PERMISSION);
+ }
+
+ /**
+ * Dispatches port addressed PDUs to interested applications
+ *
+ * @param pdus The raw PDUs making up the message
+ * @param port The destination port of the messages
+ */
+ protected void dispatchPortAddressedPdus(byte[][] pdus, int port) {
+ Uri uri = Uri.parse("sms://localhost:" + port);
+ Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
+ intent.putExtra("pdus", pdus);
+ intent.putExtra("format", getFormat());
+ dispatch(intent, RECEIVE_SMS_PERMISSION);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * RESULT_ERROR_NO_SERVICE
.
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ protected abstract void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this PendingIntent
is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be Activity.RESULT_OK for success,
+ * or one of these errors:
+ * RESULT_ERROR_GENERIC_FAILURE
+ * RESULT_ERROR_RADIO_OFF
+ * RESULT_ERROR_NULL_PDU
+ * RESULT_ERROR_NO_SERVICE
.
+ * For RESULT_ERROR_GENERIC_FAILURE
the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this PendingIntent
is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ protected abstract void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
+
+ /**
+ * Calculate the number of septets needed to encode the message.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
+ */
+ protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
+ boolean use7bitOnly);
+
+ /**
+ * Send a multi-part text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param parts an ArrayList
of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an ArrayList
of
+ * PendingIntent
s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be Activity.RESULT_OK