Merge "Require that caller holds SEND_SMS and MODIFY_PHONES_STATE permissions"
diff --git a/api/current.txt b/api/current.txt
index d543ae9..e8d0ee2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7991,7 +7991,7 @@
method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean);
method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int);
method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int);
- method public void onIntrData(android.bluetooth.BluetoothDevice, byte, byte[]);
+ method public void onInterruptData(android.bluetooth.BluetoothDevice, byte, byte[]);
method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte);
method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice);
@@ -10846,6 +10846,7 @@
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+ field public static final java.lang.String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
@@ -33389,6 +33390,7 @@
field public static final java.lang.String FEATURES = "features";
field public static final int FEATURES_HD_CALL = 4; // 0x4
field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
+ field public static final int FEATURES_RTT = 16; // 0x10
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final int FEATURES_WIFI = 8; // 0x8
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
@@ -40161,19 +40163,19 @@
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
- method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest);
method public void close();
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
- method public void download(android.telephony.mbms.DownloadRequest);
+ method public int download(android.telephony.mbms.DownloadRequest);
method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
- method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
method public void requestUpdateFileServices(java.util.List<java.lang.String>);
method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
method public void setTempFileRootDirectory(java.io.File);
- method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+ method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
@@ -40221,6 +40223,34 @@
field public static final int UNKNOWN_RSSI = 99; // 0x63
}
+ public class NetworkScan {
+ method public void stop() throws android.os.RemoteException;
+ field public static final int ERROR_INTERRUPTED = 10002; // 0x2712
+ field public static final int ERROR_INVALID_SCAN = 2; // 0x2
+ field public static final int ERROR_INVALID_SCANID = 10001; // 0x2711
+ field public static final int ERROR_MODEM_ERROR = 1; // 0x1
+ field public static final int ERROR_MODEM_UNAVAILABLE = 3; // 0x3
+ field public static final int ERROR_RADIO_INTERFACE_ERROR = 10000; // 0x2710
+ field public static final int ERROR_UNSUPPORTED = 4; // 0x4
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
+ public final class NetworkScanRequest implements android.os.Parcelable {
+ ctor public NetworkScanRequest(int, android.telephony.RadioAccessSpecifier[], int, int, boolean, int, java.util.ArrayList<java.lang.String>);
+ method public int describeContents();
+ method public boolean getIncrementalResults();
+ method public int getIncrementalResultsPeriodicity();
+ method public int getMaxSearchTime();
+ method public java.util.ArrayList<java.lang.String> getPlmns();
+ method public int getScanType();
+ method public int getSearchPeriodicity();
+ method public android.telephony.RadioAccessSpecifier[] getSpecifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.NetworkScanRequest> CREATOR;
+ field public static final int SCAN_TYPE_ONE_SHOT = 0; // 0x0
+ field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
+ }
+
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
ctor public PhoneNumberFormattingTextWatcher();
ctor public PhoneNumberFormattingTextWatcher(java.lang.String);
@@ -40313,6 +40343,121 @@
field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
}
+ public final class RadioAccessSpecifier implements android.os.Parcelable {
+ ctor public RadioAccessSpecifier(int, int[], int[]);
+ method public int describeContents();
+ method public int[] getBands();
+ method public int[] getChannels();
+ method public int getRadioAccessNetwork();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.RadioAccessSpecifier> CREATOR;
+ }
+
+ public final class RadioNetworkConstants {
+ ctor public RadioNetworkConstants();
+ }
+
+ public static final class RadioNetworkConstants.EutranBands {
+ ctor public RadioNetworkConstants.EutranBands();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_17 = 17; // 0x11
+ field public static final int BAND_18 = 18; // 0x12
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_23 = 23; // 0x17
+ field public static final int BAND_24 = 24; // 0x18
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_27 = 27; // 0x1b
+ field public static final int BAND_28 = 28; // 0x1c
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_30 = 30; // 0x1e
+ field public static final int BAND_31 = 31; // 0x1f
+ field public static final int BAND_33 = 33; // 0x21
+ field public static final int BAND_34 = 34; // 0x22
+ field public static final int BAND_35 = 35; // 0x23
+ field public static final int BAND_36 = 36; // 0x24
+ field public static final int BAND_37 = 37; // 0x25
+ field public static final int BAND_38 = 38; // 0x26
+ field public static final int BAND_39 = 39; // 0x27
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_40 = 40; // 0x28
+ field public static final int BAND_41 = 41; // 0x29
+ field public static final int BAND_42 = 42; // 0x2a
+ field public static final int BAND_43 = 43; // 0x2b
+ field public static final int BAND_44 = 44; // 0x2c
+ field public static final int BAND_45 = 45; // 0x2d
+ field public static final int BAND_46 = 46; // 0x2e
+ field public static final int BAND_47 = 47; // 0x2f
+ field public static final int BAND_48 = 48; // 0x30
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_65 = 65; // 0x41
+ field public static final int BAND_66 = 66; // 0x42
+ field public static final int BAND_68 = 68; // 0x44
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_70 = 70; // 0x46
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
+ public static final class RadioNetworkConstants.GeranBands {
+ ctor public RadioNetworkConstants.GeranBands();
+ field public static final int BAND_450 = 3; // 0x3
+ field public static final int BAND_480 = 4; // 0x4
+ field public static final int BAND_710 = 5; // 0x5
+ field public static final int BAND_750 = 6; // 0x6
+ field public static final int BAND_850 = 8; // 0x8
+ field public static final int BAND_DCS1800 = 12; // 0xc
+ field public static final int BAND_E900 = 10; // 0xa
+ field public static final int BAND_ER900 = 14; // 0xe
+ field public static final int BAND_P900 = 9; // 0x9
+ field public static final int BAND_PCS1900 = 13; // 0xd
+ field public static final int BAND_R900 = 11; // 0xb
+ field public static final int BAND_T380 = 1; // 0x1
+ field public static final int BAND_T410 = 2; // 0x2
+ field public static final int BAND_T810 = 7; // 0x7
+ }
+
+ public static final class RadioNetworkConstants.RadioAccessNetworks {
+ ctor public RadioNetworkConstants.RadioAccessNetworks();
+ field public static final int EUTRAN = 3; // 0x3
+ field public static final int GERAN = 1; // 0x1
+ field public static final int UTRAN = 2; // 0x2
+ }
+
+ public static final class RadioNetworkConstants.UtranBands {
+ ctor public RadioNetworkConstants.UtranBands();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
public class ServiceState implements android.os.Parcelable {
ctor public ServiceState();
ctor public ServiceState(android.telephony.ServiceState);
@@ -40590,12 +40735,15 @@
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
+ method public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(java.lang.String);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
method public void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+ method public void setNetworkSelectionModeAutomatic();
+ method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
@@ -40683,6 +40831,17 @@
method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int);
}
+ public final class TelephonyScanManager {
+ ctor public TelephonyScanManager();
+ }
+
+ public static abstract class TelephonyScanManager.NetworkScanCallback {
+ ctor public TelephonyScanManager.NetworkScanCallback();
+ method public void onComplete();
+ method public void onError(int);
+ method public void onResults(java.util.List<android.telephony.CellInfo>);
+ }
+
public abstract class VisualVoicemailService extends android.app.Service {
ctor public VisualVoicemailService();
method public android.os.IBinder onBind(android.content.Intent);
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index f38e462..2fab305 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -85,7 +85,7 @@
*
* @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)
* @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
- * @see BluetoothHidDeviceCallback#onIntrData(BluetoothDevice, byte, byte[])
+ * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[])
*/
public static final byte REPORT_TYPE_INPUT = (byte) 1;
public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
@@ -155,8 +155,8 @@
}
@Override
- public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
- mCallback.onIntrData(device, reportId, data);
+ public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+ mCallback.onInterruptData(device, reportId, data);
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index bd19955..e71b00f 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -106,8 +106,8 @@
* @param reportId Report Id.
* @param data Report data.
*/
- public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
- Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId);
+ public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+ Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ef8f84b..89751da 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2085,6 +2085,13 @@
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports cell-broadcast reception using the MBMS APIs.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB devices
* as the USB host.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 1b707bd..5c4a40e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -35,6 +35,9 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -888,17 +891,62 @@
keyArray[size] = key;
}
if (size >= mWarnBucketSize) {
- final int total_size = size();
+ final int totalSize = size();
Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
- + " total = " + total_size);
+ + " total = " + totalSize);
mWarnBucketSize += WARN_INCREMENT;
- if (Build.IS_DEBUGGABLE && total_size > CRASH_AT_SIZE) {
- throw new AssertionError("Binder ProxyMap has too many entries. "
- + "BinderProxy leak?");
+ if (Build.IS_DEBUGGABLE && totalSize > CRASH_AT_SIZE) {
+ diagnosticCrash();
}
}
}
+ /**
+ * Dump a histogram to the logcat, then throw an assertion error. Used to diagnose
+ * abnormally large proxy maps.
+ */
+ private void diagnosticCrash() {
+ Map<String, Integer> counts = new HashMap<>();
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> weakRef : a) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
+ }
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
+ }
+ }
+ }
+ }
+ Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+ new Map.Entry[counts.size()]);
+ Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+ -> b.getValue().compareTo(a.getValue()));
+ Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
+ int printLength = Math.min(10, sorted.length);
+ for (int i = 0; i < printLength; i++) {
+ Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
+ + sorted[i].getValue());
+ }
+
+ // Now throw an assertion.
+ final int totalSize = size();
+ throw new AssertionError("Binder ProxyMap has too many entries: " + totalSize
+ + ". BinderProxy leak?");
+ }
+
// Corresponding ArrayLists in the following two arrays always have the same size.
// They contain no empty entries. However WeakReferences in the values ArrayLists
// may have been cleared.
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 766ad84..60df467 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -212,16 +212,19 @@
public static final String FEATURES = "features";
/** Call had video. */
- public static final int FEATURES_VIDEO = 0x1;
+ public static final int FEATURES_VIDEO = 1 << 0;
/** Call was pulled externally. */
- public static final int FEATURES_PULLED_EXTERNALLY = 0x2;
+ public static final int FEATURES_PULLED_EXTERNALLY = 1 << 1;
/** Call was HD. */
- public static final int FEATURES_HD_CALL = 0x4;
+ public static final int FEATURES_HD_CALL = 1 << 2;
/** Call was WIFI call. */
- public static final int FEATURES_WIFI = 0x8;
+ public static final int FEATURES_WIFI = 1 << 3;
+
+ /** Call was on RTT at some point */
+ public static final int FEATURES_RTT = 1 << 4;
/**
* Indicates the call underwent Assisted Dialing.
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 48f3b9c..630d5f4 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4564,6 +4564,73 @@
// ---- End O-MR1 Constants, all O-MR1 constants go above this line ----
+ // OPEN: Settings > Network & Internet > Mobile network
+ // CATEGORY: SETTINGS
+ SETTINGS_MOBILE_NETWORK_CATEGORY = 1200;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Roaming
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1201;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Advanced
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1202;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Enhanced 4G LTE Mode
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1203;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Preferred network type
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1204;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Preferred network type (enabled networks)
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1205;
+
+ // OPEN: Settings > Network & Internet > Mobile network > Carrier
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_EUICC_SETTING = 1206;
+
+ // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_WIFI_CALLING = 1207;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Carrier video calling
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1208;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Automatically select network
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1209;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Network
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210;
+
+ // FIELD - Manually selected mobile network
+ FIELD_MOBILE_NETWORK = 1211;
+
+ // OPEN: Settings > Network & Internet > Mobile network > Access Point Names
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_APN_SETTINGS = 1212;
+
+ // OPEN: Settings > Network & Internet > Mobile network > Carrier settings
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1213;
+
+ // OPEN: Settings > Network & Internet > Mobile network > System select
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1214;
+
+ // OPEN: Settings > Network & Internet > Mobile network > CDMA subscription
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1215;
+
+ // ACTION: Settings > Network & Internet > Mobile network > Set up data service
+ // CATEGORY: SETTINGS
+ ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1216;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 6fcb115..4fd602f 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1436,8 +1436,9 @@
}
Log.d(this, "createConnection, connection: %s", connection);
if (connection == null) {
+ Log.i(this, "createConnection, implementation returned null connection.");
connection = Connection.createFailedConnection(
- new DisconnectCause(DisconnectCause.ERROR));
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
}
connection.setTelecomCallId(callId);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index a554c69..059a2d0 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -502,8 +502,10 @@
* Asynchronous errors through the callback may include any error not specific to the
* streaming use-case.
* @param request The request that specifies what should be downloaded.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void download(@NonNull DownloadRequest request) {
+ public int download(@NonNull DownloadRequest request) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -519,13 +521,16 @@
setTempFileRootDirectory(tempRootDirectory);
}
- writeDownloadRequestToken(request);
try {
- downloadService.download(request);
+ int result = downloadService.download(request);
+ if (result == MbmsErrors.SUCCESS) {
+ writeDownloadRequestToken(request);
+ }
+ return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
}
@@ -565,8 +570,10 @@
* @param callback The callback that should be called when the middleware has information to
* share on the download.
* @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void registerStateCallback(@NonNull DownloadRequest request,
+ public int registerStateCallback(@NonNull DownloadRequest request,
@NonNull DownloadStateCallback callback, @NonNull Handler handler) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
@@ -583,16 +590,15 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
- return;
+ return result;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return;
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
mInternalDownloadCallbacks.put(callback, internalCallback);
+ return MbmsErrors.SUCCESS;
}
/**
@@ -606,8 +612,10 @@
*
* @param request The {@link DownloadRequest} provided during registration
* @param callback The callback provided during registration.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void unregisterStateCallback(@NonNull DownloadRequest request,
+ public int unregisterStateCallback(@NonNull DownloadRequest request,
@NonNull DownloadStateCallback callback) {
try {
IMbmsDownloadService downloadService = mService.get();
@@ -617,6 +625,9 @@
InternalDownloadStateCallback internalCallback =
mInternalDownloadCallbacks.get(callback);
+ if (internalCallback == null) {
+ throw new IllegalArgumentException("Provided callback was never registered");
+ }
try {
int result = downloadService.unregisterStateCallback(request, internalCallback);
@@ -624,12 +635,12 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
+ return result;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
} finally {
InternalDownloadStateCallback internalCallback =
@@ -638,6 +649,7 @@
internalCallback.stop();
}
}
+ return MbmsErrors.SUCCESS;
}
/**
@@ -647,8 +659,10 @@
* this method will throw an {@link IllegalArgumentException}.
*
* @param downloadRequest The download request that you wish to cancel.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
+ public int cancelDownload(@NonNull DownloadRequest downloadRequest) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -660,16 +674,15 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
- return;
+ } else {
+ deleteDownloadRequestToken(downloadRequest);
}
+ return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return;
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
- deleteDownloadRequestToken(downloadRequest);
}
/**
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index f15fde8..a277212 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -19,50 +19,92 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.annotation.IntDef;
import android.util.Log;
import com.android.internal.telephony.ITelephony;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * Allows applications to request the system to perform a network scan.
- *
- * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} will
- * receive a NetworkScan which contains the callback method to stop the scan requested.
- * @hide
+ * The caller of
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)}
+ * will receive an instance of {@link NetworkScan}, which contains a callback method
+ * {@link #stop()} for stopping the in-progress scan.
*/
public class NetworkScan {
- public static final String TAG = "NetworkScan";
+ private static final String TAG = "NetworkScan";
// Below errors are mapped from RadioError which is returned from RIL. We will consolidate
// RadioErrors during the mapping if those RadioErrors mean no difference to the users.
+
+ /**
+ * Defines acceptable values of scan error code.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR_MODEM_ERROR, ERROR_INVALID_SCAN, ERROR_MODEM_UNAVAILABLE, ERROR_UNSUPPORTED,
+ ERROR_RADIO_INTERFACE_ERROR, ERROR_INVALID_SCANID, ERROR_INTERRUPTED})
+ public @interface ScanErrorCode {}
+
+ /**
+ * The RIL has successfully performed the network scan.
+ */
public static final int SUCCESS = 0; // RadioError:NONE
+
+ /**
+ * The scan has failed due to some modem errors.
+ */
public static final int ERROR_MODEM_ERROR = 1; // RadioError:RADIO_NOT_AVAILABLE
// RadioError:NO_MEMORY
// RadioError:INTERNAL_ERR
// RadioError:MODEM_ERR
// RadioError:OPERATION_NOT_ALLOWED
+
+ /**
+ * The parameters of the scan is invalid.
+ */
public static final int ERROR_INVALID_SCAN = 2; // RadioError:INVALID_ARGUMENTS
- public static final int ERROR_MODEM_BUSY = 3; // RadioError:DEVICE_IN_USE
+
+ /**
+ * The modem can not perform the scan because it is doing something else.
+ */
+ public static final int ERROR_MODEM_UNAVAILABLE = 3; // RadioError:DEVICE_IN_USE
+
+ /**
+ * The modem does not support the request scan.
+ */
public static final int ERROR_UNSUPPORTED = 4; // RadioError:REQUEST_NOT_SUPPORTED
+
// Below errors are generated at the Telephony.
- public static final int ERROR_RIL_ERROR = 10000; // Nothing or only exception is
- // returned from RIL.
- public static final int ERROR_INVALID_SCANID = 10001; // The scanId is invalid. The user is
- // either trying to stop a scan which
- // does not exist or started by others.
- public static final int ERROR_INTERRUPTED = 10002; // Scan was interrupted by another scan
- // with higher priority.
+
+ /**
+ * The RIL returns nothing or exceptions.
+ */
+ public static final int ERROR_RADIO_INTERFACE_ERROR = 10000;
+
+ /**
+ * The scan ID is invalid. The user is either trying to stop a scan which does not exist
+ * or started by others.
+ */
+ public static final int ERROR_INVALID_SCANID = 10001;
+
+ /**
+ * The scan has been interrupted by another scan with higher priority.
+ */
+ public static final int ERROR_INTERRUPTED = 10002;
+
private final int mScanId;
private final int mSubId;
/**
* Stops the network scan
*
- * This is the callback method to stop an ongoing scan. When user requests a new scan,
- * a NetworkScan object will be returned, and the user can stop the scan by calling this
- * method.
+ * Use this method to stop an ongoing scan. When user requests a new scan, a {@link NetworkScan}
+ * object will be returned, and the user can stop the scan by calling this method.
*/
public void stop() throws RemoteException {
try {
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 9674c93..ea503c3 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -16,11 +16,14 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Defines a request to peform a network scan.
@@ -28,7 +31,6 @@
* This class defines whether the network scan will be performed only once or periodically until
* cancelled, when the scan is performed periodically, the time interval is not controlled by the
* user but defined by the modem vendor.
- * @hide
*/
public final class NetworkScanRequest implements Parcelable {
@@ -54,6 +56,14 @@
/** @hide */
public static final int MAX_INCREMENTAL_PERIODICITY_SEC = 10;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SCAN_TYPE_ONE_SHOT,
+ SCAN_TYPE_PERIODIC,
+ })
+ public @interface ScanType {}
+
/** Performs the scan only once */
public static final int SCAN_TYPE_ONE_SHOT = 0;
/**
@@ -65,21 +75,21 @@
public static final int SCAN_TYPE_PERIODIC = 1;
/** Defines the type of the scan. */
- public int scanType;
+ private int mScanType;
/**
* Search periodicity (in seconds).
* Expected range for the input is [5s - 300s]
- * This value must be less than or equal to maxSearchTime
+ * This value must be less than or equal to mMaxSearchTime
*/
- public int searchPeriodicity;
+ private int mSearchPeriodicity;
/**
* Maximum duration of the periodic search (in seconds).
* Expected range for the input is [60s - 3600s]
* If the search lasts this long, it will be terminated.
*/
- public int maxSearchTime;
+ private int mMaxSearchTime;
/**
* Indicates whether the modem should report incremental
@@ -87,18 +97,18 @@
* FALSE – Incremental results are not reported.
* TRUE (default) – Incremental results are reported
*/
- public boolean incrementalResults;
+ private boolean mIncrementalResults;
/**
* Indicates the periodicity with which the modem should
* report incremental results to the client (in seconds).
* Expected range for the input is [1s - 10s]
- * This value must be less than or equal to maxSearchTime
+ * This value must be less than or equal to mMaxSearchTime
*/
- public int incrementalResultsPeriodicity;
+ private int mIncrementalResultsPeriodicity;
/** Describes the radio access technologies with bands or channels that need to be scanned. */
- public RadioAccessSpecifier[] specifiers;
+ private RadioAccessSpecifier[] mSpecifiers;
/**
* Describes the List of PLMN ids (MCC-MNC)
@@ -107,20 +117,24 @@
* If list not sent, search to be completed till end and all PLMNs found to be reported.
* Max size of array is MAX_MCC_MNC_LIST_SIZE
*/
- public ArrayList<String> mccMncs;
+ private ArrayList<String> mMccMncs;
/**
- * Creates a new NetworkScanRequest with scanType and network specifiers
+ * Creates a new NetworkScanRequest with mScanType and network mSpecifiers
*
- * @param scanType The type of the scan
+ * @param scanType The type of the scan, can be either one shot or periodic
* @param specifiers the radio network with bands / channels to be scanned
- * @param searchPeriodicity Search periodicity (in seconds)
- * @param maxSearchTime Maximum duration of the periodic search (in seconds)
+ * @param searchPeriodicity The modem will restart the scan every searchPeriodicity seconds if
+ * no network has been found, until it reaches the maxSearchTime. Only
+ * valid when scan type is periodic scan.
+ * @param maxSearchTime Maximum duration of the search (in seconds)
* @param incrementalResults Indicates whether the modem should report incremental
* results of the network scan to the client
* @param incrementalResultsPeriodicity Indicates the periodicity with which the modem should
- * report incremental results to the client (in seconds)
- * @param mccMncs Describes the List of PLMN ids (MCC-MNC)
+ * report incremental results to the client (in seconds),
+ * only valid when incrementalResults is true
+ * @param mccMncs Describes the list of PLMN ids (MCC-MNC), once any network in the list has
+ * been found, the scan will be terminated by the modem.
*/
public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers,
int searchPeriodicity,
@@ -128,19 +142,63 @@
boolean incrementalResults,
int incrementalResultsPeriodicity,
ArrayList<String> mccMncs) {
- this.scanType = scanType;
- this.specifiers = specifiers;
- this.searchPeriodicity = searchPeriodicity;
- this.maxSearchTime = maxSearchTime;
- this.incrementalResults = incrementalResults;
- this.incrementalResultsPeriodicity = incrementalResultsPeriodicity;
- if (mccMncs != null) {
- this.mccMncs = mccMncs;
+ this.mScanType = scanType;
+ this.mSpecifiers = specifiers.clone();
+ this.mSearchPeriodicity = searchPeriodicity;
+ this.mMaxSearchTime = maxSearchTime;
+ this.mIncrementalResults = incrementalResults;
+ this.mIncrementalResultsPeriodicity = incrementalResultsPeriodicity;
+ if (mMccMncs != null) {
+ this.mMccMncs = (ArrayList<String>) mccMncs.clone();
} else {
- this.mccMncs = new ArrayList<>();
+ this.mMccMncs = new ArrayList<>();
}
}
+ /** Returns the type of the scan. */
+ @ScanType
+ public int getScanType() {
+ return mScanType;
+ }
+
+ /** Returns the search periodicity in seconds. */
+ public int getSearchPeriodicity() {
+ return mSearchPeriodicity;
+ }
+
+ /** Returns maximum duration of the periodic search in seconds. */
+ public int getMaxSearchTime() {
+ return mMaxSearchTime;
+ }
+
+ /**
+ * Returns whether incremental result is enabled.
+ * FALSE – Incremental results is not enabled.
+ * TRUE – Incremental results is reported.
+ */
+ public boolean getIncrementalResults() {
+ return mIncrementalResults;
+ }
+
+ /** Returns the periodicity in seconds of incremental results. */
+ public int getIncrementalResultsPeriodicity() {
+ return mIncrementalResultsPeriodicity;
+ }
+
+ /** Returns the radio access technologies with bands or channels that need to be scanned. */
+ public RadioAccessSpecifier[] getSpecifiers() {
+ return mSpecifiers.clone();
+ }
+
+ /**
+ * Returns the List of PLMN ids (MCC-MNC) for early termination of scan.
+ * If any PLMN of this list is found, search should end at that point and
+ * results with all PLMN found till that point should be sent as response.
+ */
+ public ArrayList<String> getPlmns() {
+ return (ArrayList<String>) mMccMncs.clone();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -148,26 +206,26 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(scanType);
- dest.writeParcelableArray(specifiers, flags);
- dest.writeInt(searchPeriodicity);
- dest.writeInt(maxSearchTime);
- dest.writeBoolean(incrementalResults);
- dest.writeInt(incrementalResultsPeriodicity);
- dest.writeStringList(mccMncs);
+ dest.writeInt(mScanType);
+ dest.writeParcelableArray(mSpecifiers, flags);
+ dest.writeInt(mSearchPeriodicity);
+ dest.writeInt(mMaxSearchTime);
+ dest.writeBoolean(mIncrementalResults);
+ dest.writeInt(mIncrementalResultsPeriodicity);
+ dest.writeStringList(mMccMncs);
}
private NetworkScanRequest(Parcel in) {
- scanType = in.readInt();
- specifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
+ mScanType = in.readInt();
+ mSpecifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
Object.class.getClassLoader(),
RadioAccessSpecifier.class);
- searchPeriodicity = in.readInt();
- maxSearchTime = in.readInt();
- incrementalResults = in.readBoolean();
- incrementalResultsPeriodicity = in.readInt();
- mccMncs = new ArrayList<>();
- in.readStringList(mccMncs);
+ mSearchPeriodicity = in.readInt();
+ mMaxSearchTime = in.readInt();
+ mIncrementalResults = in.readBoolean();
+ mIncrementalResultsPeriodicity = in.readInt();
+ mMccMncs = new ArrayList<>();
+ in.readStringList(mMccMncs);
}
@Override
@@ -184,25 +242,25 @@
return false;
}
- return (scanType == nsr.scanType
- && Arrays.equals(specifiers, nsr.specifiers)
- && searchPeriodicity == nsr.searchPeriodicity
- && maxSearchTime == nsr.maxSearchTime
- && incrementalResults == nsr.incrementalResults
- && incrementalResultsPeriodicity == nsr.incrementalResultsPeriodicity
- && (((mccMncs != null)
- && mccMncs.equals(nsr.mccMncs))));
+ return (mScanType == nsr.mScanType
+ && Arrays.equals(mSpecifiers, nsr.mSpecifiers)
+ && mSearchPeriodicity == nsr.mSearchPeriodicity
+ && mMaxSearchTime == nsr.mMaxSearchTime
+ && mIncrementalResults == nsr.mIncrementalResults
+ && mIncrementalResultsPeriodicity == nsr.mIncrementalResultsPeriodicity
+ && (((mMccMncs != null)
+ && mMccMncs.equals(nsr.mMccMncs))));
}
@Override
public int hashCode () {
- return ((scanType * 31)
- + (Arrays.hashCode(specifiers)) * 37
- + (searchPeriodicity * 41)
- + (maxSearchTime * 43)
- + ((incrementalResults == true? 1 : 0) * 47)
- + (incrementalResultsPeriodicity * 53)
- + (mccMncs.hashCode() * 59));
+ return ((mScanType * 31)
+ + (Arrays.hashCode(mSpecifiers)) * 37
+ + (mSearchPeriodicity * 41)
+ + (mMaxSearchTime * 43)
+ + ((mIncrementalResults == true? 1 : 0) * 47)
+ + (mIncrementalResultsPeriodicity * 53)
+ + (mMccMncs.hashCode() * 59));
}
public static final Creator<NetworkScanRequest> CREATOR =
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 33ce8b4..5412c61 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -25,34 +25,40 @@
* Describes a particular radio access network to be scanned.
*
* The scan can be performed on either bands or channels for a specific radio access network type.
- * @hide
*/
public final class RadioAccessSpecifier implements Parcelable {
/**
* The radio access network that needs to be scanned
*
+ * This parameter must be provided or else the scan will be rejected.
+ *
* See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
*/
- public int radioAccessNetwork;
+ private int mRadioAccessNetwork;
/**
* The frequency bands that need to be scanned
*
- * bands must be used together with radioAccessNetwork
+ * When no specific bands are specified (empty array or null), all the frequency bands
+ * supported by the modem will be scanned.
*
* See {@link RadioNetworkConstants} for details.
*/
- public int[] bands;
+ private int[] mBands;
/**
* The frequency channels that need to be scanned
*
- * channels must be used together with radioAccessNetwork
+ * When any specific channels are provided for scan, the corresponding frequency bands that
+ * contains those channels must also be provided, or else the channels will be ignored.
*
- * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+ * When no specific channels are specified (empty array or null), all the frequency channels
+ * supported by the modem will be scanned.
+ *
+ * See {@link RadioNetworkConstants} for details.
*/
- public int[] channels;
+ private int[] mChannels;
/**
* Creates a new RadioAccessSpecifier with radio network, bands and channels
@@ -65,9 +71,34 @@
* @param channels the frequency bands to be scanned
*/
public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
- this.radioAccessNetwork = ran;
- this.bands = bands;
- this.channels = channels;
+ this.mRadioAccessNetwork = ran;
+ this.mBands = bands.clone();
+ this.mChannels = channels.clone();
+ }
+
+ /**
+ * Returns the radio access network that needs to be scanned.
+ *
+ * The returned value is define in {@link RadioNetworkConstants.RadioAccessNetworks};
+ */
+ public int getRadioAccessNetwork() {
+ return mRadioAccessNetwork;
+ }
+
+ /**
+ * Returns the frequency bands that need to be scanned.
+ *
+ * The returned value is defined in either of {@link RadioNetworkConstants.GeranBands},
+ * {@link RadioNetworkConstants.UtranBands} and {@link RadioNetworkConstants.EutranBands}, and
+ * it depends on the returned value of {@link #getRadioAccessNetwork()}.
+ */
+ public int[] getBands() {
+ return mBands.clone();
+ }
+
+ /** Returns the frequency channels that need to be scanned. */
+ public int[] getChannels() {
+ return mChannels.clone();
}
public static final Parcelable.Creator<RadioAccessSpecifier> CREATOR =
@@ -90,15 +121,15 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(radioAccessNetwork);
- dest.writeIntArray(bands);
- dest.writeIntArray(channels);
+ dest.writeInt(mRadioAccessNetwork);
+ dest.writeIntArray(mBands);
+ dest.writeIntArray(mChannels);
}
private RadioAccessSpecifier(Parcel in) {
- radioAccessNetwork = in.readInt();
- bands = in.createIntArray();
- channels = in.createIntArray();
+ mRadioAccessNetwork = in.readInt();
+ mBands = in.createIntArray();
+ mChannels = in.createIntArray();
}
@Override
@@ -115,15 +146,15 @@
return false;
}
- return (radioAccessNetwork == ras.radioAccessNetwork
- && Arrays.equals(bands, ras.bands)
- && Arrays.equals(channels, ras.channels));
+ return (mRadioAccessNetwork == ras.mRadioAccessNetwork
+ && Arrays.equals(mBands, ras.mBands)
+ && Arrays.equals(mChannels, ras.mChannels));
}
@Override
public int hashCode () {
- return ((radioAccessNetwork * 31)
- + (Arrays.hashCode(bands) * 37)
- + (Arrays.hashCode(channels)) * 39);
+ return ((mRadioAccessNetwork * 31)
+ + (Arrays.hashCode(mBands) * 37)
+ + (Arrays.hashCode(mChannels)) * 39);
}
}
diff --git a/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/RadioNetworkConstants.java
index 1a9072d..5f5dd82 100644
--- a/telephony/java/android/telephony/RadioNetworkConstants.java
+++ b/telephony/java/android/telephony/RadioNetworkConstants.java
@@ -18,7 +18,6 @@
/**
* Contains radio access network related constants.
- * @hide
*/
public final class RadioNetworkConstants {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 82464c1..7245dac 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4764,15 +4764,14 @@
* Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
- *
- * @hide
- * TODO: Add an overload that takes no args.
*/
- public void setNetworkSelectionModeAutomatic(int subId) {
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setNetworkSelectionModeAutomatic() {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.setNetworkSelectionModeAutomatic(subId);
+ if (telephony != null) {
+ telephony.setNetworkSelectionModeAutomatic(getSubId());
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
} catch (NullPointerException ex) {
@@ -4820,9 +4819,9 @@
*
* @param request Contains all the RAT with bands/channels that need to be scanned.
* @param callback Returns network scan results or errors.
- * @return A NetworkScan obj which contains a callback which can stop the scan.
- * @hide
+ * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public NetworkScan requestNetworkScan(
NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
synchronized (this) {
@@ -4841,15 +4840,20 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
- * @hide
- * TODO: Add an overload that takes no args.
+ * @param operatorNumeric the PLMN ID of the network to select.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
+ * @return true on success; false on any failure.
*/
- public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator,
- boolean persistSelection) {
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection);
+ if (telephony != null) {
+ return telephony.setNetworkSelectionModeManual(
+ getSubId(), operatorNumeric, persistSelection);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
} catch (NullPointerException ex) {
@@ -4874,8 +4878,9 @@
public boolean setPreferredNetworkType(int subId, int networkType) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.setPreferredNetworkType(subId, networkType);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -5854,45 +5859,6 @@
return false;
}
- /**
- * This function retrieves value for setting "name+subId", and if that is not found
- * retrieves value for setting "name", and if that is not found throws
- * SettingNotFoundException
- *
- * @hide
- */
- public static int getIntWithSubId(ContentResolver cr, String name, int subId)
- throws SettingNotFoundException {
- try {
- return Settings.Global.getInt(cr, name + subId);
- } catch (SettingNotFoundException e) {
- try {
- int val = Settings.Global.getInt(cr, name);
- Settings.Global.putInt(cr, name + subId, val);
-
- /* We are now moving from 'setting' to 'setting+subId', and using the value stored
- * for 'setting' as default. Reset the default (since it may have a user set
- * value). */
- int default_val = val;
- if (name.equals(Settings.Global.MOBILE_DATA)) {
- default_val = "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.mobiledata", "true")) ? 1 : 0;
- } else if (name.equals(Settings.Global.DATA_ROAMING)) {
- default_val = "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.dataroaming", "false")) ? 1 : 0;
- }
-
- if (default_val != val) {
- Settings.Global.putInt(cr, name, default_val);
- }
-
- return val;
- } catch (SettingNotFoundException exc) {
- throw new SettingNotFoundException(name);
- }
- }
- }
-
/**
* Returns the IMS Registration Status
* @hide
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 7bcdcdc..c182e34 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -38,7 +38,6 @@
/**
* Manages the radio access network scan requests and callbacks.
- * @hide
*/
public final class TelephonyScanManager {
@@ -55,7 +54,8 @@
public static final int CALLBACK_SCAN_COMPLETE = 3;
/**
- * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
+ * The caller of
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
* implement and provide this callback so that the scan results or errors can be returned.
*/
public static abstract class NetworkScanCallback {
@@ -75,8 +75,10 @@
*
* This callback will be called whenever there is any error about the scan, and the scan
* will be terminated. onComplete() will NOT be called.
+ *
+ * @param error Error code when the scan is failed, as defined in {@link NetworkScan}.
*/
- public void onError(int error) {}
+ public void onError(@NetworkScan.ScanErrorCode int error) {}
}
private static class NetworkScanInfo {
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index 8529f52..f78e7a6 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -51,8 +51,8 @@
/** @hide */
public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
String newServiceId, Date start, Date end) {
- if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName)
- || newLocales == null || newLocales.isEmpty() || TextUtils.isEmpty(newServiceId)
+ if (newNames == null || newClassName == null
+ || newLocales == null || newServiceId == null
|| start == null || end == null) {
throw new IllegalArgumentException("Bad ServiceInfo construction");
}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 6ad54c1..4f6f68c 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -104,6 +104,9 @@
// MT : No action from user after alerting the call
public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
+ //Call was blocked by call barring
+ public static final int CODE_CALL_BARRED = 240;
+
//Call failures for FDN
public static final int CODE_FDN_BLOCKED = 241;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fd6091a..64083e3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -846,13 +846,13 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* @param subId the id of the subscription.
- * @param operatorInfo the operator to attach to.
- * @param persistSelection should the selection persist till reboot or its
- * turned off? Will also result in notification being not shown to
- * the user if the signal is lost.
+ * @param operatorNumeric the PLMN of the operator to attach to.
+ * @param persistSelection Whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
* @return true if the request suceeded.
*/
- boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator,
+ boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,
boolean persistSelection);
/**
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 99a82ad..9f8b3a8 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -32,6 +32,12 @@
public class IccUtils {
static final String LOG_TAG="IccUtils";
+ // A table mapping from a number to a hex character for fast encoding hex strings.
+ private static final char[] HEX_CHARS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+
/**
* Many fields in GSM SIM's are stored as nibble-swizzled BCD
*
@@ -62,6 +68,41 @@
}
/**
+ * Converts a bcd byte array to String with offset 0 and byte array length.
+ */
+ public static String bcdToString(byte[] data) {
+ return bcdToString(data, 0, data.length);
+ }
+
+ /**
+ * Converts BCD string to bytes.
+ *
+ * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+ */
+ public static byte[] bcdToBytes(String bcd) {
+ byte[] output = new byte[(bcd.length() + 1) / 2];
+ bcdToBytes(bcd, output);
+ return output;
+ }
+
+ /**
+ * Converts BCD string to bytes and put it into the given byte array.
+ *
+ * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+ * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
+ * converted. If the array size is more than needed, the rest of array remains unchanged.
+ */
+ public static void bcdToBytes(String bcd, byte[] bytes) {
+ if (bcd.length() % 2 != 0) {
+ bcd += "0";
+ }
+ int size = Math.min(bytes.length * 2, bcd.length());
+ for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
+ bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
+ }
+ }
+
+ /**
* PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
* Returns a concatenated string of MCC+MNC, stripping
* all invalid character 'f'
@@ -94,10 +135,10 @@
int v;
v = data[i] & 0xf;
- ret.append("0123456789abcdef".charAt(v));
+ ret.append(HEX_CHARS[v]);
v = (data[i] >> 4) & 0xf;
- ret.append("0123456789abcdef".charAt(v));
+ ret.append(HEX_CHARS[v]);
}
return ret.toString();
@@ -305,7 +346,7 @@
return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
}
- static int
+ public static int
hexCharToInt(char c) {
if (c >= '0' && c <= '9') return (c - '0');
if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
@@ -361,11 +402,11 @@
b = 0x0f & (bytes[i] >> 4);
- ret.append("0123456789abcdef".charAt(b));
+ ret.append(HEX_CHARS[b]);
b = 0x0f & bytes[i];
- ret.append("0123456789abcdef".charAt(b));
+ ret.append(HEX_CHARS[b]);
}
return ret.toString();
@@ -416,7 +457,6 @@
if ((data[offset] & 0x40) != 0) {
// FIXME(mkf) add country initials here
-
}
return ret;
@@ -575,4 +615,239 @@
}
return iccId.substring( 0, position );
}
+
+ /**
+ * Converts a series of bytes to an integer. This method currently only supports positive 32-bit
+ * integers.
+ *
+ * @param src The source bytes.
+ * @param offset The position of the first byte of the data to be converted. The data is base
+ * 256 with the most significant digit first.
+ * @param length The length of the data to be converted. It must be <= 4.
+ * @throws IllegalArgumentException If {@code length} is bigger than 4 or {@code src} cannot be
+ * parsed as a positive integer.
+ * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
+ * exceeds the bounds of {@code src}.
+ */
+ public static int bytesToInt(byte[] src, int offset, int length) {
+ if (length > 4) {
+ throw new IllegalArgumentException(
+ "length must be <= 4 (only 32-bit integer supported): " + length);
+ }
+ if (offset < 0 || length < 0 || offset + length > src.length) {
+ throw new IndexOutOfBoundsException(
+ "Out of the bounds: src=["
+ + src.length
+ + "], offset="
+ + offset
+ + ", length="
+ + length);
+ }
+ int result = 0;
+ for (int i = 0; i < length; i++) {
+ result = (result << 8) | (src[offset + i] & 0xFF);
+ }
+ if (result < 0) {
+ throw new IllegalArgumentException(
+ "src cannot be parsed as a positive integer: " + result);
+ }
+ return result;
+ }
+
+ /**
+ * Converts a series of bytes to a raw long variable which can be both positive and negative.
+ * This method currently only supports 64-bit long variable.
+ *
+ * @param src The source bytes.
+ * @param offset The position of the first byte of the data to be converted. The data is base
+ * 256 with the most significant digit first.
+ * @param length The length of the data to be converted. It must be <= 8.
+ * @throws IllegalArgumentException If {@code length} is bigger than 8.
+ * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
+ * exceeds the bounds of {@code src}.
+ */
+ public static long bytesToRawLong(byte[] src, int offset, int length) {
+ if (length > 8) {
+ throw new IllegalArgumentException(
+ "length must be <= 8 (only 64-bit long supported): " + length);
+ }
+ if (offset < 0 || length < 0 || offset + length > src.length) {
+ throw new IndexOutOfBoundsException(
+ "Out of the bounds: src=["
+ + src.length
+ + "], offset="
+ + offset
+ + ", length="
+ + length);
+ }
+ long result = 0;
+ for (int i = 0; i < length; i++) {
+ result = (result << 8) | (src[offset + i] & 0xFF);
+ }
+ return result;
+ }
+
+ /**
+ * Converts an integer to a new byte array with base 256 and the most significant digit first.
+ *
+ * @throws IllegalArgumentException If {@code value} is negative.
+ */
+ public static byte[] unsignedIntToBytes(int value) {
+ if (value < 0) {
+ throw new IllegalArgumentException("value must be 0 or positive: " + value);
+ }
+ byte[] bytes = new byte[byteNumForUnsignedInt(value)];
+ unsignedIntToBytes(value, bytes, 0);
+ return bytes;
+ }
+
+ /**
+ * Converts an integer to a new byte array with base 256 and the most significant digit first.
+ * The first byte's highest bit is used for sign. If the most significant digit is larger than
+ * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
+ * negative values.
+ *
+ * @throws IllegalArgumentException If {@code value} is negative.
+ */
+ public static byte[] signedIntToBytes(int value) {
+ if (value < 0) {
+ throw new IllegalArgumentException("value must be 0 or positive: " + value);
+ }
+ byte[] bytes = new byte[byteNumForSignedInt(value)];
+ signedIntToBytes(value, bytes, 0);
+ return bytes;
+ }
+
+ /**
+ * Converts an integer to a series of bytes with base 256 and the most significant digit first.
+ *
+ * @param value The integer to be converted.
+ * @param dest The destination byte array.
+ * @param offset The start offset of the byte array.
+ * @return The number of byte needeed.
+ * @throws IllegalArgumentException If {@code value} is negative.
+ * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
+ */
+ public static int unsignedIntToBytes(int value, byte[] dest, int offset) {
+ return intToBytes(value, dest, offset, false);
+ }
+
+ /**
+ * Converts an integer to a series of bytes with base 256 and the most significant digit first.
+ * The first byte's highest bit is used for sign. If the most significant digit is larger than
+ * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
+ * negative values.
+ *
+ * @throws IllegalArgumentException If {@code value} is negative.
+ * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
+ */
+ public static int signedIntToBytes(int value, byte[] dest, int offset) {
+ return intToBytes(value, dest, offset, true);
+ }
+
+ /**
+ * Calculates the number of required bytes to represent {@code value}. The bytes will be base
+ * 256 with the most significant digit first.
+ *
+ * @throws IllegalArgumentException If {@code value} is negative.
+ */
+ public static int byteNumForUnsignedInt(int value) {
+ return byteNumForInt(value, false);
+ }
+
+ /**
+ * Calculates the number of required bytes to represent {@code value}. The bytes will be base
+ * 256 with the most significant digit first. If the most significant digit is larger than 127,
+ * an extra byte (0) will be prepended before it. This method currently only supports positive
+ * integers.
+ *
+ * @throws IllegalArgumentException If {@code value} is negative.
+ */
+ public static int byteNumForSignedInt(int value) {
+ return byteNumForInt(value, true);
+ }
+
+ private static int intToBytes(int value, byte[] dest, int offset, boolean signed) {
+ int l = byteNumForInt(value, signed);
+ if (offset < 0 || offset + l > dest.length) {
+ throw new IndexOutOfBoundsException("Not enough space to write. Required bytes: " + l);
+ }
+ for (int i = l - 1, v = value; i >= 0; i--, v >>>= 8) {
+ byte b = (byte) (v & 0xFF);
+ dest[offset + i] = b;
+ }
+ return l;
+ }
+
+ private static int byteNumForInt(int value, boolean signed) {
+ if (value < 0) {
+ throw new IllegalArgumentException("value must be 0 or positive: " + value);
+ }
+ if (signed) {
+ if (value <= 0x7F) {
+ return 1;
+ }
+ if (value <= 0x7FFF) {
+ return 2;
+ }
+ if (value <= 0x7FFFFF) {
+ return 3;
+ }
+ } else {
+ if (value <= 0xFF) {
+ return 1;
+ }
+ if (value <= 0xFFFF) {
+ return 2;
+ }
+ if (value <= 0xFFFFFF) {
+ return 3;
+ }
+ }
+ return 4;
+ }
+
+
+ /**
+ * Counts the number of trailing zero bits of a byte.
+ */
+ public static byte countTrailingZeros(byte b) {
+ if (b == 0) {
+ return 8;
+ }
+ int v = b & 0xFF;
+ byte c = 7;
+ if ((v & 0x0F) != 0) {
+ c -= 4;
+ }
+ if ((v & 0x33) != 0) {
+ c -= 2;
+ }
+ if ((v & 0x55) != 0) {
+ c -= 1;
+ }
+ return c;
+ }
+
+ /**
+ * Converts a byte to a hex string.
+ */
+ public static String byteToHex(byte b) {
+ return new String(new char[] {HEX_CHARS[(b & 0xFF) >>> 4], HEX_CHARS[b & 0xF]});
+ }
+
+ /**
+ * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
+ * hex number, 0 will be returned.
+ */
+ private static byte charToByte(char c) {
+ if (c >= 0x30 && c <= 0x39) {
+ return (byte) (c - 0x30);
+ } else if (c >= 0x41 && c <= 0x46) {
+ return (byte) (c - 0x37);
+ } else if (c >= 0x61 && c <= 0x66) {
+ return (byte) (c - 0x57);
+ }
+ return 0;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Decoder.java b/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Decoder.java
new file mode 100644
index 0000000..1ad0b66
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Decoder.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc.asn1;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+/**
+ * This represents a decoder helping decode an array of bytes or a hex string into
+ * {@link Asn1Node}s. This class tracks the next position for decoding. This class is not
+ * thread-safe.
+ */
+public final class Asn1Decoder {
+ // Source byte array.
+ private final byte[] mSrc;
+ // Next position of the byte in the source array for decoding.
+ private int mPosition;
+ // Exclusive end of the range in the array for decoding.
+ private final int mEnd;
+
+ /** Creates a decoder on a hex string. */
+ public Asn1Decoder(String hex) {
+ this(IccUtils.hexStringToBytes(hex));
+ }
+
+ /** Creates a decoder on a byte array. */
+ public Asn1Decoder(byte[] src) {
+ this(src, 0, src.length);
+ }
+
+ /**
+ * Creates a decoder on a byte array slice.
+ *
+ * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
+ * exceeds the bounds of {@code bytes}.
+ */
+ public Asn1Decoder(byte[] bytes, int offset, int length) {
+ if (offset < 0 || length < 0 || offset + length > bytes.length) {
+ throw new IndexOutOfBoundsException(
+ "Out of the bounds: bytes=["
+ + bytes.length
+ + "], offset="
+ + offset
+ + ", length="
+ + length);
+ }
+ mSrc = bytes;
+ mPosition = offset;
+ mEnd = offset + length;
+ }
+
+ /** @return The next start position for decoding. */
+ public int getPosition() {
+ return mPosition;
+ }
+
+ /** Returns whether the node has a next node. */
+ public boolean hasNextNode() {
+ return mPosition < mEnd;
+ }
+
+ /**
+ * Parses the next node. If the node is a constructed node, its children will be parsed only
+ * when they are accessed, e.g., though {@link Asn1Node#getChildren()}.
+ *
+ * @return The next decoded {@link Asn1Node}. If success, the next decoding position will also
+ * be updated. If any error happens, e.g., moving over the end position, {@code null}
+ * will be returned and the next decoding position won't be modified.
+ * @throws InvalidAsn1DataException If the bytes cannot be parsed.
+ */
+ public Asn1Node nextNode() throws InvalidAsn1DataException {
+ if (mPosition >= mEnd) {
+ throw new IllegalStateException("No bytes to parse.");
+ }
+
+ int offset = mPosition;
+
+ // Extracts the tag.
+ int tagStart = offset;
+ byte b = mSrc[offset++];
+ if ((b & 0x1F) == 0x1F) {
+ // High-tag-number form
+ while (offset < mEnd && (mSrc[offset++] & 0x80) != 0) {
+ // Do nothing.
+ }
+ }
+ if (offset >= mEnd) {
+ // No length bytes or the tag is too long.
+ throw new InvalidAsn1DataException(0, "Invalid length at position: " + offset);
+ }
+ int tag;
+ try {
+ tag = IccUtils.bytesToInt(mSrc, tagStart, offset - tagStart);
+ } catch (IllegalArgumentException e) {
+ // Cannot parse the tag as an integer.
+ throw new InvalidAsn1DataException(0, "Cannot parse tag at position: " + tagStart, e);
+ }
+
+ // Extracts the length.
+ int dataLen;
+ b = mSrc[offset++];
+ if ((b & 0x80) == 0) {
+ // Short-form length
+ dataLen = b;
+ } else {
+ // Long-form length
+ int lenLen = b & 0x7F;
+ if (offset + lenLen > mEnd) {
+ // No enough bytes for the long-form length
+ throw new InvalidAsn1DataException(
+ tag, "Cannot parse length at position: " + offset);
+ }
+ try {
+ dataLen = IccUtils.bytesToInt(mSrc, offset, lenLen);
+ } catch (IllegalArgumentException e) {
+ // Cannot parse the data length as an integer.
+ throw new InvalidAsn1DataException(
+ tag, "Cannot parse length at position: " + offset, e);
+ }
+ offset += lenLen;
+ }
+ if (offset + dataLen > mEnd) {
+ // No enough data left.
+ throw new InvalidAsn1DataException(
+ tag,
+ "Incomplete data at position: "
+ + offset
+ + ", expected bytes: "
+ + dataLen
+ + ", actual bytes: "
+ + (mEnd - offset));
+ }
+
+ Asn1Node root = new Asn1Node(tag, mSrc, offset, dataLen);
+ mPosition = offset + dataLen;
+ return root;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Node.java b/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Node.java
new file mode 100644
index 0000000..5eb1d5c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/uicc/asn1/Asn1Node.java
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc.asn1;
+
+import android.annotation.Nullable;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This represents a primitive or constructed data defined by ASN.1. A constructed node can have
+ * child nodes. A non-constructed node can have a value. This class is read-only. To build a node,
+ * you can use the {@link #newBuilder(int)} method to get a {@link Builder} instance. This class is
+ * not thread-safe.
+ */
+public final class Asn1Node {
+ private static final int INT_BYTES = Integer.SIZE / Byte.SIZE;
+ private static final List<Asn1Node> EMPTY_NODE_LIST = Collections.emptyList();
+
+ // Bytes for boolean values.
+ private static final byte[] TRUE_BYTES = new byte[] {-1};
+ private static final byte[] FALSE_BYTES = new byte[] {0};
+
+ /**
+ * This class is used to build an Asn1Node instance of a constructed tag. This class is not
+ * thread-safe.
+ */
+ public static final class Builder {
+ private final int mTag;
+ private final List<Asn1Node> mChildren;
+
+ private Builder(int tag) {
+ if (!isConstructedTag(tag)) {
+ throw new IllegalArgumentException(
+ "Builder should be created for a constructed tag: " + tag);
+ }
+ mTag = tag;
+ mChildren = new ArrayList<>();
+ }
+
+ /**
+ * Adds a child from an existing node.
+ *
+ * @return This builder.
+ * @throws IllegalArgumentException If the child is a non-existing node.
+ */
+ public Builder addChild(Asn1Node child) {
+ mChildren.add(child);
+ return this;
+ }
+
+ /**
+ * Adds a child from another builder. The child will be built with the call to this method,
+ * and any changes to the child builder after the call to this method doesn't have effect.
+ *
+ * @return This builder.
+ */
+ public Builder addChild(Builder child) {
+ mChildren.add(child.build());
+ return this;
+ }
+
+ /**
+ * Adds children from bytes. This method calls {@link Asn1Decoder} to verify the {@code
+ * encodedBytes} and adds all nodes parsed from it as children.
+ *
+ * @return This builder.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public Builder addChildren(byte[] encodedBytes) throws InvalidAsn1DataException {
+ Asn1Decoder subDecoder = new Asn1Decoder(encodedBytes, 0, encodedBytes.length);
+ while (subDecoder.hasNextNode()) {
+ mChildren.add(subDecoder.nextNode());
+ }
+ return this;
+ }
+
+ /**
+ * Adds a child of non-constructed tag with an integer as the data.
+ *
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsInteger(int tag, int value) {
+ if (isConstructedTag(tag)) {
+ throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
+ }
+ byte[] dataBytes = IccUtils.signedIntToBytes(value);
+ addChild(new Asn1Node(tag, dataBytes, 0, dataBytes.length));
+ return this;
+ }
+
+ /**
+ * Adds a child of non-constructed tag with a string as the data.
+ *
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsString(int tag, String value) {
+ if (isConstructedTag(tag)) {
+ throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
+ }
+ byte[] dataBytes = value.getBytes(StandardCharsets.UTF_8);
+ addChild(new Asn1Node(tag, dataBytes, 0, dataBytes.length));
+ return this;
+ }
+
+ /**
+ * Adds a child of non-constructed tag with a byte array as the data.
+ *
+ * @param value The value will be owned by this node.
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsBytes(int tag, byte[] value) {
+ if (isConstructedTag(tag)) {
+ throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
+ }
+ addChild(new Asn1Node(tag, value, 0, value.length));
+ return this;
+ }
+
+ /**
+ * Adds a child of non-constructed tag with a byte array as the data from a hex string.
+ *
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsBytesFromHex(int tag, String hex) {
+ return addChildAsBytes(tag, IccUtils.hexStringToBytes(hex));
+ }
+
+ /**
+ * Adds a child of non-constructed tag with bits as the data.
+ *
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsBits(int tag, int value) {
+ if (isConstructedTag(tag)) {
+ throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
+ }
+ // Always allocate 5 bytes for simplicity.
+ byte[] dataBytes = new byte[INT_BYTES + 1];
+ // Puts the integer into the byte[1-4].
+ value = Integer.reverse(value);
+ int dataLength = 0;
+ for (int i = 1; i < dataBytes.length; i++) {
+ dataBytes[i] = (byte) (value >> ((INT_BYTES - i) * Byte.SIZE));
+ if (dataBytes[i] != 0) {
+ dataLength = i;
+ }
+ }
+ dataLength++;
+ // The first byte is the number of trailing zeros of the last byte.
+ dataBytes[0] = IccUtils.countTrailingZeros(dataBytes[dataLength - 1]);
+ addChild(new Asn1Node(tag, dataBytes, 0, dataLength));
+ return this;
+ }
+
+ /**
+ * Adds a child of non-constructed tag with a boolean as the data.
+ *
+ * @return This builder.
+ * @throws IllegalStateException If the {@code tag} is not constructed..
+ */
+ public Builder addChildAsBoolean(int tag, boolean value) {
+ if (isConstructedTag(tag)) {
+ throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
+ }
+ addChild(new Asn1Node(tag, value ? TRUE_BYTES : FALSE_BYTES, 0, 1));
+ return this;
+ }
+
+ /** Builds the node. */
+ public Asn1Node build() {
+ return new Asn1Node(mTag, mChildren);
+ }
+ }
+
+ private final int mTag;
+ private final boolean mConstructed;
+ // Do not use this field directly in the methods other than the constructor and encoding
+ // methods (e.g., toBytes()), but always use getChildren() instead.
+ private final List<Asn1Node> mChildren;
+
+ // Byte array that actually holds the data. For a non-constructed node, this stores its actual
+ // value. If the value is not set, this is null. For constructed node, this stores encoded data
+ // of its children, which will be decoded on the first call to getChildren().
+ private @Nullable byte[] mDataBytes;
+ // Offset of the data in above byte array.
+ private int mDataOffset;
+ // Length of the data in above byte array. If it's a constructed node, this is always the total
+ // length of all its children.
+ private int mDataLength;
+ // Length of the total bytes required to encode this node.
+ private int mEncodedLength;
+
+ /**
+ * Creates a new ASN.1 data node builder with the given tag. The tag is an encoded tag including
+ * the tag class, tag number, and constructed mask.
+ */
+ public static Builder newBuilder(int tag) {
+ return new Builder(tag);
+ }
+
+ private static boolean isConstructedTag(int tag) {
+ // Constructed mask is at the 6th bit.
+ byte[] tagBytes = IccUtils.unsignedIntToBytes(tag);
+ return (tagBytes[0] & 0x20) != 0;
+ }
+
+ private static int calculateEncodedBytesNumForLength(int length) {
+ // Constructed mask is at the 6th bit.
+ int len = 1;
+ if (length > 127) {
+ len += IccUtils.byteNumForUnsignedInt(length);
+ }
+ return len;
+ }
+
+ /**
+ * Creates a node with given data bytes. If it is a constructed node, its children will be
+ * parsed when they are visited.
+ */
+ Asn1Node(int tag, @Nullable byte[] src, int offset, int length) {
+ mTag = tag;
+ // Constructed mask is at the 6th bit.
+ mConstructed = isConstructedTag(tag);
+ mDataBytes = src;
+ mDataOffset = offset;
+ mDataLength = length;
+ mChildren = mConstructed ? new ArrayList<Asn1Node>() : EMPTY_NODE_LIST;
+ mEncodedLength =
+ IccUtils.byteNumForUnsignedInt(mTag)
+ + calculateEncodedBytesNumForLength(mDataLength)
+ + mDataLength;
+ }
+
+ /** Creates a constructed node with given children. */
+ private Asn1Node(int tag, List<Asn1Node> children) {
+ mTag = tag;
+ mConstructed = true;
+ mChildren = children;
+
+ mDataLength = 0;
+ int size = children.size();
+ for (int i = 0; i < size; i++) {
+ mDataLength += children.get(i).mEncodedLength;
+ }
+ mEncodedLength =
+ IccUtils.byteNumForUnsignedInt(mTag)
+ + calculateEncodedBytesNumForLength(mDataLength)
+ + mDataLength;
+ }
+
+ public int getTag() {
+ return mTag;
+ }
+
+ public boolean isConstructed() {
+ return mConstructed;
+ }
+
+ /**
+ * Tests if a node has a child.
+ *
+ * @param tag The tag of an immediate child.
+ * @param tags The tags of lineal descendant.
+ */
+ public boolean hasChild(int tag, int... tags) throws InvalidAsn1DataException {
+ try {
+ getChild(tag, tags);
+ } catch (TagNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets the first child node having the given {@code tag} and {@code tags}.
+ *
+ * @param tag The tag of an immediate child.
+ * @param tags The tags of lineal descendant.
+ * @throws TagNotFoundException If the child cannot be found.
+ */
+ public Asn1Node getChild(int tag, int... tags)
+ throws TagNotFoundException, InvalidAsn1DataException {
+ if (!mConstructed) {
+ throw new TagNotFoundException(tag);
+ }
+ int index = 0;
+ Asn1Node node = this;
+ while (node != null) {
+ List<Asn1Node> children = node.getChildren();
+ int size = children.size();
+ Asn1Node foundChild = null;
+ for (int i = 0; i < size; i++) {
+ Asn1Node child = children.get(i);
+ if (child.getTag() == tag) {
+ foundChild = child;
+ break;
+ }
+ }
+ node = foundChild;
+ if (index >= tags.length) {
+ break;
+ }
+ tag = tags[index++];
+ }
+ if (node == null) {
+ throw new TagNotFoundException(tag);
+ }
+ return node;
+ }
+
+ /**
+ * Gets all child nodes which have the given {@code tag}.
+ *
+ * @return If this is primitive or no such children are found, an empty list will be returned.
+ */
+ public List<Asn1Node> getChildren(int tag)
+ throws TagNotFoundException, InvalidAsn1DataException {
+ if (!mConstructed) {
+ return EMPTY_NODE_LIST;
+ }
+
+ List<Asn1Node> children = getChildren();
+ if (children.isEmpty()) {
+ return EMPTY_NODE_LIST;
+ }
+ List<Asn1Node> output = new ArrayList<>();
+ int size = children.size();
+ for (int i = 0; i < size; i++) {
+ Asn1Node child = children.get(i);
+ if (child.getTag() == tag) {
+ output.add(child);
+ }
+ }
+ return output.isEmpty() ? EMPTY_NODE_LIST : output;
+ }
+
+ /**
+ * Gets all child nodes of this node. If it's a constructed node having encoded data, it's
+ * children will be decoded here.
+ *
+ * @return If this is primitive, an empty list will be returned. Do not modify the returned list
+ * directly.
+ */
+ public List<Asn1Node> getChildren() throws InvalidAsn1DataException {
+ if (!mConstructed) {
+ return EMPTY_NODE_LIST;
+ }
+
+ if (mDataBytes != null) {
+ Asn1Decoder subDecoder = new Asn1Decoder(mDataBytes, mDataOffset, mDataLength);
+ while (subDecoder.hasNextNode()) {
+ mChildren.add(subDecoder.nextNode());
+ }
+ mDataBytes = null;
+ mDataOffset = 0;
+ }
+ return mChildren;
+ }
+
+ /** @return Whether this node has a value. False will be returned for a constructed node. */
+ public boolean hasValue() {
+ return !mConstructed && mDataBytes != null;
+ }
+
+ /**
+ * @return The data as an integer. If the data length is larger than 4, only the first 4 bytes
+ * will be parsed.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public int asInteger() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ try {
+ return IccUtils.bytesToInt(mDataBytes, mDataOffset, mDataLength);
+ } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
+ throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
+ }
+ }
+
+ /**
+ * @return The data as a long variable which can be both positive and negative. If the data
+ * length is larger than 8, only the first 8 bytes will be parsed.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public long asRawLong() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ try {
+ return IccUtils.bytesToRawLong(mDataBytes, mDataOffset, mDataLength);
+ } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
+ throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
+ }
+ }
+
+ /**
+ * @return The data as a string in UTF-8 encoding.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public String asString() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ try {
+ return new String(mDataBytes, mDataOffset, mDataLength, StandardCharsets.UTF_8);
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
+ }
+ }
+
+ /**
+ * @return The data as a byte array.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public byte[] asBytes() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ byte[] output = new byte[mDataLength];
+ try {
+ System.arraycopy(mDataBytes, mDataOffset, output, 0, mDataLength);
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
+ }
+ return output;
+ }
+
+ /**
+ * Gets the data as an integer for BIT STRING. DER actually stores the bits in a reversed order.
+ * The returned integer here has the order fixed (first bit is at the lowest position). This
+ * method currently only support at most 32 bits which fit in an integer.
+ *
+ * @return The data as an integer. If this is constructed, a {@code null} will be returned.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public int asBits() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ int bits;
+ try {
+ bits = IccUtils.bytesToInt(mDataBytes, mDataOffset + 1, mDataLength - 1);
+ } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
+ throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
+ }
+ for (int i = mDataLength - 1; i < INT_BYTES; i++) {
+ bits <<= Byte.SIZE;
+ }
+ return Integer.reverse(bits);
+ }
+
+ /**
+ * @return The data as a boolean.
+ * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
+ */
+ public boolean asBoolean() throws InvalidAsn1DataException {
+ if (mConstructed) {
+ throw new IllegalStateException("Cannot get value of a constructed node.");
+ }
+ if (mDataBytes == null) {
+ throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
+ }
+ if (mDataLength != 1) {
+ throw new InvalidAsn1DataException(
+ mTag, "Cannot parse data bytes as boolean: length=" + mDataLength);
+ }
+ if (mDataOffset < 0 || mDataOffset >= mDataBytes.length) {
+ throw new InvalidAsn1DataException(
+ mTag,
+ "Cannot parse data bytes.",
+ new ArrayIndexOutOfBoundsException(mDataOffset));
+ }
+ // ASN.1 has "true" as 0xFF.
+ if (mDataBytes[mDataOffset] == -1) {
+ return Boolean.TRUE;
+ } else if (mDataBytes[mDataOffset] == 0) {
+ return Boolean.FALSE;
+ }
+ throw new InvalidAsn1DataException(
+ mTag, "Cannot parse data bytes as boolean: " + mDataBytes[mDataOffset]);
+ }
+
+ /** @return The number of required bytes for encoding this node in DER. */
+ public int getEncodedLength() {
+ return mEncodedLength;
+ }
+
+ /** @return The number of required bytes for encoding this node's data in DER. */
+ public int getDataLength() {
+ return mDataLength;
+ }
+
+ /**
+ * Writes the DER encoded bytes of this node into a byte array. The number of written bytes is
+ * {@link #getEncodedLength()}.
+ *
+ * @throws IndexOutOfBoundsException If the {@code dest} doesn't have enough space to write.
+ */
+ public void writeToBytes(byte[] dest, int offset) {
+ if (offset < 0 || offset + mEncodedLength > dest.length) {
+ throw new IndexOutOfBoundsException(
+ "Not enough space to write. Required bytes: " + mEncodedLength);
+ }
+ write(dest, offset);
+ }
+
+ /** Writes the DER encoded bytes of this node into a new byte array. */
+ public byte[] toBytes() {
+ byte[] dest = new byte[mEncodedLength];
+ write(dest, 0);
+ return dest;
+ }
+
+ /** Gets a hex string representing the DER encoded bytes of this node. */
+ public String toHex() {
+ return IccUtils.bytesToHexString(toBytes());
+ }
+
+ /** Gets header (tag + length) as hex string. */
+ public String getHeadAsHex() {
+ String headHex = IccUtils.bytesToHexString(IccUtils.unsignedIntToBytes(mTag));
+ if (mDataLength <= 127) {
+ headHex += IccUtils.byteToHex((byte) mDataLength);
+ } else {
+ byte[] lenBytes = IccUtils.unsignedIntToBytes(mDataLength);
+ headHex += IccUtils.byteToHex((byte) (lenBytes.length | 0x80));
+ headHex += IccUtils.bytesToHexString(lenBytes);
+ }
+ return headHex;
+ }
+
+ /** Returns the new offset where to write the next node data. */
+ private int write(byte[] dest, int offset) {
+ // Writes the tag.
+ offset += IccUtils.unsignedIntToBytes(mTag, dest, offset);
+ // Writes the length.
+ if (mDataLength <= 127) {
+ dest[offset++] = (byte) mDataLength;
+ } else {
+ // Bytes required for encoding the length
+ int lenLen = IccUtils.unsignedIntToBytes(mDataLength, dest, ++offset);
+ dest[offset - 1] = (byte) (lenLen | 0x80);
+ offset += lenLen;
+ }
+ // Writes the data.
+ if (mConstructed && mDataBytes == null) {
+ int size = mChildren.size();
+ for (int i = 0; i < size; i++) {
+ Asn1Node child = mChildren.get(i);
+ offset = child.write(dest, offset);
+ }
+ } else if (mDataBytes != null) {
+ System.arraycopy(mDataBytes, mDataOffset, dest, offset, mDataLength);
+ offset += mDataLength;
+ }
+ return offset;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/asn1/InvalidAsn1DataException.java b/telephony/java/com/android/internal/telephony/uicc/asn1/InvalidAsn1DataException.java
new file mode 100644
index 0000000..c151468
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/uicc/asn1/InvalidAsn1DataException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc.asn1;
+
+/**
+ * Exception for invalid ASN.1 data in DER encoding which cannot be parsed as a node or a specific
+ * data type.
+ */
+public class InvalidAsn1DataException extends Exception {
+ private final int mTag;
+
+ public InvalidAsn1DataException(int tag, String message) {
+ super(message);
+ mTag = tag;
+ }
+
+ public InvalidAsn1DataException(int tag, String message, Throwable throwable) {
+ super(message, throwable);
+ mTag = tag;
+ }
+
+ /** @return The tag which has the invalid data. */
+ public int getTag() {
+ return mTag;
+ }
+
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " (tag=" + mTag + ")";
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/asn1/TagNotFoundException.java b/telephony/java/com/android/internal/telephony/uicc/asn1/TagNotFoundException.java
new file mode 100644
index 0000000..f79021e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/uicc/asn1/TagNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc.asn1;
+
+/**
+ * Exception for getting a child of a {@link Asn1Node} with a non-existing tag.
+ */
+public class TagNotFoundException extends Exception {
+ private final int mTag;
+
+ public TagNotFoundException(int tag) {
+ mTag = tag;
+ }
+
+ /** @return The tag which has the invalid data. */
+ public int getTag() {
+ return mTag;
+ }
+
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " (tag=" + mTag + ")";
+ }
+}