Merge "Fix temporary file name strlcpy size."
diff --git a/Android.bp b/Android.bp
index 2c1fce3..eb5240e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -464,6 +464,7 @@
"telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
"telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
@@ -484,13 +485,11 @@
"telephony/java/com/android/ims/internal/IImsService.aidl",
"telephony/java/com/android/ims/internal/IImsServiceController.aidl",
"telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl",
- "telephony/java/com/android/ims/internal/IImsSmsFeature.aidl",
"telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl",
"telephony/java/com/android/ims/internal/IImsUt.aidl",
"telephony/java/com/android/ims/internal/IImsUtListener.aidl",
"telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl",
"telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl",
- "telephony/java/com/android/ims/internal/ISmsListener.aidl",
"telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl",
"telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl",
"telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl",
diff --git a/api/current.txt b/api/current.txt
index 5f43bf3..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";
@@ -39099,6 +39101,7 @@
field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3
field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2
field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4
+ field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5; // 0x5
}
public static class Call.Details {
@@ -40160,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";
@@ -40220,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);
@@ -40312,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);
@@ -40589,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);
@@ -40682,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/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 9180112..95e7f60 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -67,4 +67,13 @@
/** Unregisters a callback on data usage. */
void unregisterUsageRequest(in DataUsageRequest request);
+ /** Get the uid stats information since boot */
+ long getUidStats(int uid, int type);
+
+ /** Get the iface stats information since boot */
+ long getIfaceStats(String iface, int type);
+
+ /** Get the total network stats information since boot */
+ long getTotalStats(int type);
+
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index d701550..196a3bc 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -505,7 +505,12 @@
public static long getMobileTcpRxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- final long stat = nativeGetIfaceStat(iface, TYPE_TCP_RX_PACKETS);
+ long stat = UNSUPPORTED;
+ try {
+ stat = getStatsService().getIfaceStats(iface, TYPE_TCP_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
if (stat != UNSUPPORTED) {
total += stat;
}
@@ -517,7 +522,12 @@
public static long getMobileTcpTxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- final long stat = nativeGetIfaceStat(iface, TYPE_TCP_TX_PACKETS);
+ long stat = UNSUPPORTED;
+ try {
+ stat = getStatsService().getIfaceStats(iface, TYPE_TCP_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
if (stat != UNSUPPORTED) {
total += stat;
}
@@ -527,46 +537,78 @@
/** {@hide} */
public static long getTxPackets(String iface) {
- return nativeGetIfaceStat(iface, TYPE_TX_PACKETS);
+ try {
+ return getStatsService().getIfaceStats(iface, TYPE_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
public static long getRxPackets(String iface) {
- return nativeGetIfaceStat(iface, TYPE_RX_PACKETS);
+ try {
+ return getStatsService().getIfaceStats(iface, TYPE_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
public static long getTxBytes(String iface) {
- return nativeGetIfaceStat(iface, TYPE_TX_BYTES);
+ try {
+ return getStatsService().getIfaceStats(iface, TYPE_TX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
public static long getRxBytes(String iface) {
- return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
+ try {
+ return getStatsService().getIfaceStats(iface, TYPE_RX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
@TestApi
public static long getLoopbackTxPackets() {
- return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_PACKETS);
+ try {
+ return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
@TestApi
public static long getLoopbackRxPackets() {
- return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_PACKETS);
+ try {
+ return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
@TestApi
public static long getLoopbackTxBytes() {
- return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_BYTES);
+ try {
+ return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
@TestApi
public static long getLoopbackRxBytes() {
- return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_BYTES);
+ try {
+ return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -579,7 +621,11 @@
* return {@link #UNSUPPORTED} on devices where statistics aren't available.
*/
public static long getTotalTxPackets() {
- return nativeGetTotalStat(TYPE_TX_PACKETS);
+ try {
+ return getStatsService().getTotalStats(TYPE_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -592,7 +638,11 @@
* return {@link #UNSUPPORTED} on devices where statistics aren't available.
*/
public static long getTotalRxPackets() {
- return nativeGetTotalStat(TYPE_RX_PACKETS);
+ try {
+ return getStatsService().getTotalStats(TYPE_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -605,7 +655,11 @@
* return {@link #UNSUPPORTED} on devices where statistics aren't available.
*/
public static long getTotalTxBytes() {
- return nativeGetTotalStat(TYPE_TX_BYTES);
+ try {
+ return getStatsService().getTotalStats(TYPE_TX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -618,7 +672,11 @@
* return {@link #UNSUPPORTED} on devices where statistics aren't available.
*/
public static long getTotalRxBytes() {
- return nativeGetTotalStat(TYPE_RX_BYTES);
+ try {
+ return getStatsService().getTotalStats(TYPE_RX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -644,7 +702,11 @@
// unsupported value. The real filtering is done at the kernel level.
final int callingUid = android.os.Process.myUid();
if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- return nativeGetUidStat(uid, TYPE_TX_BYTES);
+ try {
+ return getStatsService().getUidStats(uid, TYPE_TX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
} else {
return UNSUPPORTED;
}
@@ -673,7 +735,11 @@
// unsupported value. The real filtering is done at the kernel level.
final int callingUid = android.os.Process.myUid();
if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- return nativeGetUidStat(uid, TYPE_RX_BYTES);
+ try {
+ return getStatsService().getUidStats(uid, TYPE_RX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
} else {
return UNSUPPORTED;
}
@@ -702,7 +768,11 @@
// unsupported value. The real filtering is done at the kernel level.
final int callingUid = android.os.Process.myUid();
if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- return nativeGetUidStat(uid, TYPE_TX_PACKETS);
+ try {
+ return getStatsService().getUidStats(uid, TYPE_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
} else {
return UNSUPPORTED;
}
@@ -731,7 +801,11 @@
// unsupported value. The real filtering is done at the kernel level.
final int callingUid = android.os.Process.myUid();
if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- return nativeGetUidStat(uid, TYPE_RX_PACKETS);
+ try {
+ return getStatsService().getUidStats(uid, TYPE_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
} else {
return UNSUPPORTED;
}
@@ -859,8 +933,4 @@
private static final int TYPE_TX_PACKETS = 3;
private static final int TYPE_TCP_RX_PACKETS = 4;
private static final int TYPE_TCP_TX_PACKETS = 5;
-
- private static native long nativeGetTotalStat(int type);
- private static native long nativeGetIfaceStat(String iface, int type);
- private static native long nativeGetUidStat(int uid, int type);
}
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/core/jni/Android.bp b/core/jni/Android.bp
index 336fee1..551d54a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -96,7 +96,6 @@
"android_os_VintfRuntimeInfo.cpp",
"android_net_LocalSocketImpl.cpp",
"android_net_NetUtils.cpp",
- "android_net_TrafficStats.cpp",
"android_nio_utils.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9059862..047fa84 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -173,7 +173,6 @@
extern int register_android_os_SharedMemory(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
-extern int register_android_net_TrafficStats(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
@@ -643,6 +642,7 @@
char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX];
char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];
std::string fingerprintBuf;
+ char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
@@ -765,9 +765,15 @@
* Set suspend=y to pause during VM init and use android ADB transport.
*/
if (zygote) {
- addOption("-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y");
+ addOption("-XjdwpOptions:suspend=n,server=y");
}
+ // Set the JDWP provider. By default let the runtime choose.
+ parseRuntimeOption("dalvik.vm.jdwp-provider",
+ jdwpProviderBuf,
+ "-XjdwpProvider:",
+ "default");
+
parseRuntimeOption("dalvik.vm.lockprof.threshold",
lockProfThresholdBuf,
"-Xlockprofthreshold:");
@@ -1419,7 +1425,6 @@
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
REG_JNI(register_android_net_NetworkUtils),
- REG_JNI(register_android_net_TrafficStats),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index c1e8c98..5bfde78 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -10,7 +10,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- frameworks-base-testutils
+ frameworks-base-testutils \
+ mockito-target-minus-junit4
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 533771a..13617f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -265,7 +265,9 @@
mStatusBarIconController.getTransitionsController().dump(fd, pw, args);
pw.println();
pw.println(" NavigationBarTransitionsController:");
- mNavigationBarController.dump(fd, pw, args);
+ if (mNavigationBarController != null) {
+ mNavigationBarController.dump(fd, pw, args);
+ }
pw.println();
}
}
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/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a764808..d3ab125 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -988,12 +988,6 @@
sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
mUidFdTagger.tag(sockFd, callingUid);
- if (port != 0) {
- Log.v(TAG, "Binding to port " + port);
- Os.bind(sockFd, INADDR_ANY, port);
- } else {
- port = bindToRandomPort(sockFd);
- }
// This code is common to both the unspecified and specified port cases
Os.setsockoptInt(
sockFd,
@@ -1001,6 +995,14 @@
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
+ mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+ if (port != 0) {
+ Log.v(TAG, "Binding to port " + port);
+ Os.bind(sockFd, INADDR_ANY, port);
+ } else {
+ port = bindToRandomPort(sockFd);
+ }
+
userRecord.mEncapSocketRecords.put(
resourceId,
new RefcountedResource<EncapSocketRecord>(
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 3af5265..db61ef5 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -873,6 +873,21 @@
}
}
+ @Override
+ public long getUidStats(int uid, int type) {
+ return nativeGetUidStat(uid, type);
+ }
+
+ @Override
+ public long getIfaceStats(String iface, int type) {
+ return nativeGetIfaceStat(iface, type);
+ }
+
+ @Override
+ public long getTotalStats(int type) {
+ return nativeGetTotalStat(type);
+ }
+
/**
* Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to
* reflect current {@link #mPersistThreshold} value. Always defers to
@@ -1626,4 +1641,15 @@
return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
}
}
+
+ private static int TYPE_RX_BYTES;
+ private static int TYPE_RX_PACKETS;
+ private static int TYPE_TX_BYTES;
+ private static int TYPE_TX_PACKETS;
+ private static int TYPE_TCP_RX_PACKETS;
+ private static int TYPE_TCP_TX_PACKETS;
+
+ private static native long nativeGetTotalStat(int type);
+ private static native long nativeGetIfaceStat(String iface, int type);
+ private static native long nativeGetUidStat(int uid, int type);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 27acaee..04fd3e3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -33,6 +33,7 @@
"com_android_server_location_ContextHubService.cpp",
"com_android_server_location_GnssLocationProvider.cpp",
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
+ "com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_storage_AppFuseBridge.cpp",
diff --git a/core/jni/android_net_TrafficStats.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
similarity index 79%
rename from core/jni/android_net_TrafficStats.cpp
rename to services/core/jni/com_android_server_net_NetworkStatsService.cpp
index d0c237d..8de24e5 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "TrafficStats"
+#define LOG_TAG "NetworkStatsNative"
#include <dirent.h>
#include <errno.h>
@@ -191,8 +191,24 @@
{"nativeGetUidStat", "(II)J", (void*) getUidStat},
};
-int register_android_net_TrafficStats(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/net/TrafficStats", gMethods, NELEM(gMethods));
+int register_android_server_net_NetworkStatsService(JNIEnv* env) {
+ jclass netStatsService = env->FindClass("com/android/server/net/NetworkStatsService");
+ jfieldID rxBytesId = env->GetStaticFieldID(netStatsService, "TYPE_RX_BYTES", "I");
+ jfieldID rxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_RX_PACKETS", "I");
+ jfieldID txBytesId = env->GetStaticFieldID(netStatsService, "TYPE_TX_BYTES", "I");
+ jfieldID txPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TX_PACKETS", "I");
+ jfieldID tcpRxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_RX_PACKETS", "I");
+ jfieldID tcpTxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_TX_PACKETS", "I");
+
+ env->SetStaticIntField(netStatsService, rxBytesId, RX_BYTES);
+ env->SetStaticIntField(netStatsService, rxPacketsId, RX_PACKETS);
+ env->SetStaticIntField(netStatsService, txBytesId, TX_BYTES);
+ env->SetStaticIntField(netStatsService, txPacketsId, TX_PACKETS);
+ env->SetStaticIntField(netStatsService, tcpRxPacketsId, TCP_RX_PACKETS);
+ env->SetStaticIntField(netStatsService, tcpTxPacketsId, TCP_TX_PACKETS);
+
+ return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods,
+ NELEM(gMethods));
}
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index e8ef168..8dfbbf3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,7 @@
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_server_net_NetworkStatsService(JNIEnv* env);
};
using namespace android;
@@ -98,6 +99,7 @@
register_android_server_SyntheticPasswordManager(env);
register_android_server_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
+ register_android_server_net_NetworkStatsService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 7ea42da..28d6d07 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -831,6 +831,77 @@
}
@Test
+ public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlag()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
public void testFindGroupNotificationsLocked() throws Exception {
// make sure the same notification can be found in both lists and returned
final NotificationRecord group1 = generateNotificationRecord(
@@ -1692,4 +1763,157 @@
preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance());
}
+
+ @Test
+ public void testCancelAllNotifications_CancelsNoClearFlagOnGoing() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mNotificationManagerService.addNotification(notif);
+ mNotificationManagerService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0,
+ Notification.FLAG_ONGOING_EVENT, true, notif.getUserId(), 0, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_NoClearFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mNotificationManagerService.addNotification(notif);
+ mNotificationManagerService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
+ notif.getUserId(), 0, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllOnClearAll_OnGoingFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mNotificationManagerService.addNotification(notif);
+
+ mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ notif.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_OnGoingFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_OnGoingFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ // Flags aren't checked in the cancel children call,
+ // so all of the notifications are canceled (expected value is 0)
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_OnGoingFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 5cd2044..2091101 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -868,7 +868,8 @@
* @hide
*/
@IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED,
- HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED})
+ HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED,
+ HANDOVER_FAILURE_ONGOING_EMERG_CALL})
@Retention(RetentionPolicy.SOURCE)
public @interface HandoverFailureErrors {}
@@ -896,6 +897,12 @@
*/
public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4;
+ /**
+ * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there
+ * is ongoing emergency call.
+ */
+ public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5;
+
/**
* Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
@@ -1945,6 +1952,15 @@
}
}
+ /** {@hide} */
+ final void internalOnHandoverFailed(int error) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onHandoverFailed(call, error));
+ }
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index da8ac5e..4fd602f 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -143,6 +144,7 @@
private static final String SESSION_START_RTT = "CS.+RTT";
private static final String SESSION_STOP_RTT = "CS.-RTT";
private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
+ private static final String SESSION_HANDOVER_FAILED = "CS.haF";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -172,6 +174,7 @@
private static final int MSG_ON_STOP_RTT = 27;
private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
+ private static final int MSG_HANDOVER_FAILED = 32;
private static Connection sNullConnection;
@@ -275,6 +278,22 @@
}
@Override
+ public void handoverFailed(String callId, ConnectionRequest request, int reason,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = request;
+ args.arg3 = Log.createSubsession();
+ args.arg4 = reason;
+ mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void abort(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_ABORT);
try {
@@ -723,6 +742,36 @@
}
break;
}
+ case MSG_HANDOVER_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+ SESSION_HANDOVER_FAILED);
+ try {
+ final String id = (String) args.arg1;
+ final ConnectionRequest request = (ConnectionRequest) args.arg2;
+ final int reason = (int) args.arg4;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER
+ + SESSION_HANDOVER_FAILED + ".pICR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ handoverFailed(id, request, reason);
+ }
+ }.prepare());
+ } else {
+ Log.i(this, "createConnectionFailed %s", id);
+ handoverFailed(id, request, reason);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_ABORT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT);
@@ -1371,13 +1420,25 @@
isIncoming,
isUnknown);
- Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
- : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
- : onCreateOutgoingConnection(callManagerAccount, request);
+ Connection connection = null;
+ if (getApplicationContext().getApplicationInfo().targetSdkVersion >
+ Build.VERSION_CODES.O_MR1 && request.getExtras() != null &&
+ request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) {
+ if (!isIncoming) {
+ connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
+ } else {
+ connection = onCreateIncomingHandoverConnection(callManagerAccount, request);
+ }
+ } else {
+ connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
+ : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
+ : onCreateOutgoingConnection(callManagerAccount, request);
+ }
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);
@@ -1442,6 +1503,13 @@
}
}
+ private void handoverFailed(final String callId, final ConnectionRequest request,
+ int reason) {
+
+ Log.i(this, "handoverFailed %s", callId);
+ onHandoverFailed(request, reason);
+ }
+
/**
* Called by Telecom when the creation of a new Connection has completed and it is now added
* to Telecom.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index d558bba..74fa62d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -80,6 +80,7 @@
private static final int MSG_ON_CONNECTION_EVENT = 9;
private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
+ private static final int MSG_ON_HANDOVER_FAILED = 12;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -150,6 +151,12 @@
mPhone.internalOnRttInitiationFailure(callId, reason);
break;
}
+ case MSG_ON_HANDOVER_FAILED: {
+ String callId = (String) msg.obj;
+ int error = msg.arg1;
+ mPhone.internalOnHandoverFailed(callId, error);
+ break;
+ }
default:
break;
}
@@ -225,6 +232,11 @@
public void onRttInitiationFailure(String callId, int reason) {
mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, callId).sendToTarget();
}
+
+ @Override
+ public void onHandoverFailed(String callId, int error) {
+ mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 421b1a4..b5394b9 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -223,6 +223,13 @@
}
}
+ final void internalOnHandoverFailed(String callId, int error) {
+ Call call = mCallByTelecomCallId.get(callId);
+ if (call != null) {
+ call.internalOnHandoverFailed(error);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e428286..732d00d 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -100,4 +100,7 @@
void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall,
in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
+
+ void handoverFailed(String callId, in ConnectionRequest request,
+ int error, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index e8cf8e9..110109e 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -54,4 +54,6 @@
void onRttUpgradeRequest(String callId, int id);
void onRttInitiationFailure(String callId, int reason);
+
+ void onHandoverFailed(String callId, int error);
}
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/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6029995..78a4c65 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -339,6 +339,7 @@
* Send a text based SMS without writing it into the SMS Provider.
*
* <p>Requires Permission:
+ * {@link android.Manifest.permission#SEND_SMS} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
* privileges.
* </p>
@@ -347,7 +348,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.SEND_SMS
+ })
public void sendTextMessageWithoutPersisting(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -1123,6 +1127,8 @@
// SMS send failure result codes
+ /** No error. {@hide}*/
+ static public final int RESULT_ERROR_NONE = 0;
/** Generic failure cause */
static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
/** Failed because radio was explicitly turned off */
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/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a13af5f..84d0087 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -168,12 +168,10 @@
public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
private final Context mContext;
- private final IEuiccController mController;
/** @hide */
public EuiccManager(Context context) {
mContext = context;
- mController = IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
}
/**
@@ -189,7 +187,7 @@
public boolean isEnabled() {
// In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
// restrictions.
- return mController != null;
+ return getIEuiccController() != null;
}
/**
@@ -206,7 +204,7 @@
return null;
}
try {
- return mController.getEid();
+ return getIEuiccController().getEid();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -232,7 +230,7 @@
return;
}
try {
- mController.downloadSubscription(subscription, switchAfterDownload,
+ getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -296,7 +294,7 @@
return;
}
try {
- mController.continueOperation(resolutionIntent, resolutionExtras);
+ getIEuiccController().continueOperation(resolutionIntent, resolutionExtras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -328,7 +326,7 @@
return;
}
try {
- mController.getDownloadableSubscriptionMetadata(
+ getIEuiccController().getDownloadableSubscriptionMetadata(
subscription, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -358,7 +356,7 @@
return;
}
try {
- mController.getDefaultDownloadableSubscriptionList(
+ getIEuiccController().getDefaultDownloadableSubscriptionList(
mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -377,7 +375,7 @@
return null;
}
try {
- return mController.getEuiccInfo();
+ return getIEuiccController().getEuiccInfo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -402,7 +400,7 @@
return;
}
try {
- mController.deleteSubscription(
+ getIEuiccController().deleteSubscription(
subscriptionId, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -429,7 +427,7 @@
return;
}
try {
- mController.switchToSubscription(
+ getIEuiccController().switchToSubscription(
subscriptionId, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -455,7 +453,8 @@
return;
}
try {
- mController.updateSubscriptionNickname(subscriptionId, nickname, callbackIntent);
+ getIEuiccController().updateSubscriptionNickname(
+ subscriptionId, nickname, callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -477,7 +476,7 @@
return;
}
try {
- mController.eraseSubscriptions(callbackIntent);
+ getIEuiccController().eraseSubscriptions(callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -507,7 +506,7 @@
return;
}
try {
- mController.retainSubscriptionsForFactoryReset(callbackIntent);
+ getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -520,4 +519,8 @@
// Caller canceled the callback; do nothing.
}
}
+
+ private static IEuiccController getIEuiccController() {
+ return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/SmsFeature.java b/telephony/java/android/telephony/ims/feature/SmsFeature.java
deleted file mode 100644
index c1366db..0000000
--- a/telephony/java/android/telephony/ims/feature/SmsFeature.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-import android.annotation.SystemApi;
-import android.os.RemoteException;
-import com.android.ims.internal.IImsSmsFeature;
-import com.android.ims.internal.ISmsListener;
-
-/**
- * Base implementation of SMS over IMS functionality.
- *
- * @hide
- */
-public class SmsFeature extends ImsFeature {
- /**
- * SMS over IMS format is 3gpp.
- */
- public static final int IMS_SMS_FORMAT_3GPP = 1;
-
- /**
- * SMS over IMS format is 3gpp2.
- */
- public static final int IMS_SMS_FORMAT_3GPP2 = 2;
-
- /**
- * Message was sent successfully.
- */
- public static final int SEND_STATUS_OK = 1;
-
- /**
- * IMS provider failed to send the message and platform should not retry falling back to sending
- * the message using the radio.
- */
- public static final int SEND_STATUS_ERROR = 2;
-
- /**
- * IMS provider failed to send the message and platform should retry again after setting TP-RD bit
- * to high.
- */
- public static final int SEND_STATUS_ERROR_RETRY = 3;
-
- /**
- * IMS provider failed to send the message and platform should retry falling back to sending
- * the message using the radio.
- */
- public static final int SEND_STATUS_ERROR_FALLBACK = 4;
-
- /**
- * Message was delivered successfully.
- */
- public static final int DELIVER_STATUS_OK = 1;
-
- /**
- * Message was not delivered.
- */
- public static final int DELIVER_STATUS_ERROR = 2;
-
- // Lock for feature synchronization
- private final Object mLock = new Object();
- private ISmsListener mSmsListener;
-
- private final IImsSmsFeature mIImsSmsBinder = new IImsSmsFeature.Stub() {
- @Override
- public void registerSmsListener(ISmsListener listener) {
- synchronized (mLock) {
- SmsFeature.this.registerSmsListener(listener);
- }
- }
-
- @Override
- public void sendSms(int format, int messageRef, boolean retry, byte[] pdu) {
- synchronized (mLock) {
- SmsFeature.this.sendSms(format, messageRef, retry, pdu);
- }
- }
-
- @Override
- public void acknowledgeSms(int messageRef, int result) {
- synchronized (mLock) {
- SmsFeature.this.acknowledgeSms(messageRef, result);
- }
- }
-
- @Override
- public int getSmsFormat() {
- synchronized (mLock) {
- return SmsFeature.this.getSmsFormat();
- }
- }
- };
-
- /**
- * Registers a listener responsible for handling tasks like delivering messages.
-
- * @param listener listener to register.
- *
- * @hide
- */
- @SystemApi
- public final void registerSmsListener(ISmsListener listener) {
- synchronized (mLock) {
- mSmsListener = listener;
- }
- }
-
- /**
- * This method will be triggered by the platform when the user attempts to send an SMS. This
- * method should be implemented by the IMS providers to provide implementation of sending an SMS
- * over IMS.
- *
- * @param format the format of the message. One of {@link #IMS_SMS_FORMAT_3GPP} or
- * {@link #IMS_SMS_FORMAT_3GPP2}
- * @param messageRef the message reference.
- * @param retry whether it is a retry of an already attempted message or not.
- * @param pdu PDUs representing the contents of the message.
- */
- public void sendSms(int format, int messageRef, boolean isRetry, byte[] pdu) {
- }
-
- /**
- * This method will be triggered by the platform after {@link #deliverSms(int, byte[])} has been
- * called to deliver the result to the IMS provider. It will also be triggered after
- * {@link #setSentSmsResult(int, int)} has been called to provide the result of the operation.
- *
- * @param result Should be {@link #DELIVER_STATUS_OK} if the message was delivered successfully,
- * {@link #DELIVER_STATUS_ERROR} otherwise.
- * @param messageRef the message reference.
- */
- public void acknowledgeSms(int messageRef, int result) {
-
- }
-
- /**
- * This method should be triggered by the IMS providers when there is an incoming message. The
- * platform will deliver the message to the messages database and notify the IMS provider of the
- * result by calling {@link #acknowledgeSms(int)}.
- *
- * This method must not be called before {@link #onFeatureReady()} is called.
- *
- * @param format the format of the message.One of {@link #IMS_SMS_FORMAT_3GPP} or
- * {@link #IMS_SMS_FORMAT_3GPP2}
- * @param pdu PDUs representing the contents of the message.
- * @throws IllegalStateException if called before {@link #onFeatureReady()}
- */
- public final void deliverSms(int format, byte[] pdu) throws IllegalStateException {
- // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
- // otherwise.
- try {
- mSmsListener.deliverSms(format, pdu);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * This method should be triggered by the IMS providers to pass the result of the sent message
- * to the platform.
- *
- * This method must not be called before {@link #onFeatureReady()} is called.
- *
- * @param messageRef the message reference.
- * @param result One of {@link #SEND_STATUS_OK}, {@link #SEND_STATUS_ERROR},
- * {@link #SEND_STATUS_ERROR_RETRY}, {@link #SEND_STATUS_ERROR_FALLBACK}
- * @throws IllegalStateException if called before {@link #onFeatureReady()}
- */
- public final void setSentSmsResult(int messageRef, int result) throws IllegalStateException {
- // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
- // otherwise.
- try {
- mSmsListener.setSentSmsResult(messageRef, result);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Sets the status report of the sent message.
- *
- * @param format Should be {@link #IMS_SMS_FORMAT_3GPP} or {@link #IMS_SMS_FORMAT_3GPP2}
- * @param pdu PDUs representing the content of the status report.
- * @throws IllegalStateException if called before {@link #onFeatureReady()}
- */
- public final void setSentSmsStatusReport(int format, byte[] pdu) {
- // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
- // otherwise.
- try {
- mSmsListener.setSentSmsStatusReport(format, pdu);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Returns the SMS format. Default is {@link #IMS_SMS_FORMAT_3GPP} unless overridden by IMS
- * Provider.
- *
- * @return sms format.
- */
- public int getSmsFormat() {
- return IMS_SMS_FORMAT_3GPP;
- }
-
- /**
- * {@inheritDoc}
- */
- public void onFeatureReady() {
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onFeatureRemoved() {
-
- }
-
- /**
- * @hide
- */
- @Override
- public final IImsSmsFeature getBinder() {
- return mIImsSmsBinder;
- }
-}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/SmsImplBase.java b/telephony/java/android/telephony/ims/internal/SmsImplBase.java
new file mode 100644
index 0000000..47414cf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/SmsImplBase.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.ims.internal.aidl.IImsSmsListener;
+import android.telephony.ims.internal.feature.MmTelFeature;
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for SMS over IMS.
+ *
+ * Any service wishing to provide SMS over IMS should extend this class and implement all methods
+ * that the service supports.
+ * @hide
+ */
+public class SmsImplBase {
+ private static final String LOG_TAG = "SmsImplBase";
+
+ @IntDef({
+ SEND_STATUS_OK,
+ SEND_STATUS_ERROR,
+ SEND_STATUS_ERROR_RETRY,
+ SEND_STATUS_ERROR_FALLBACK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SendStatusResult {}
+ /**
+ * Message was sent successfully.
+ */
+ public static final int SEND_STATUS_OK = 1;
+
+ /**
+ * IMS provider failed to send the message and platform should not retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR = 2;
+
+ /**
+ * IMS provider failed to send the message and platform should retry again after setting TP-RD bit
+ * to high.
+ */
+ public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+ /**
+ * IMS provider failed to send the message and platform should retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+ @IntDef({
+ DELIVER_STATUS_OK,
+ DELIVER_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeliverStatusResult {}
+ /**
+ * Message was delivered successfully.
+ */
+ public static final int DELIVER_STATUS_OK = 1;
+
+ /**
+ * Message was not delivered.
+ */
+ public static final int DELIVER_STATUS_ERROR = 2;
+
+ @IntDef({
+ STATUS_REPORT_STATUS_OK,
+ STATUS_REPORT_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusReportResult {}
+
+ /**
+ * Status Report was set successfully.
+ */
+ public static final int STATUS_REPORT_STATUS_OK = 1;
+
+ /**
+ * Error while setting status report.
+ */
+ public static final int STATUS_REPORT_STATUS_ERROR = 2;
+
+
+ // Lock for feature synchronization
+ private final Object mLock = new Object();
+ private IImsSmsListener mListener;
+
+ /**
+ * Registers a listener responsible for handling tasks like delivering messages.
+ *
+ * @param listener listener to register.
+ *
+ * @hide
+ */
+ public final void registerSmsListener(IImsSmsListener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform when the user attempts to send an SMS. This
+ * method should be implemented by the IMS providers to provide implementation of sending an SMS
+ * over IMS.
+ *
+ * @param smsc the Short Message Service Center address.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageRef the message reference.
+ * @param isRetry whether it is a retry of an already attempted message or not.
+ * @param pdu PDUs representing the contents of the message.
+ */
+ public void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
+ // Base implementation returns error. Should be overridden.
+ try {
+ onSendSmsResult(messageRef, SEND_STATUS_ERROR, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform after {@link #onSmsReceived(String, byte[])} has
+ * been called to deliver the result to the IMS provider.
+ *
+ * @param result result of delivering the message. Valid values are defined in
+ * {@link DeliverStatusResult}
+ * @param messageRef the message reference or -1 of unavailable.
+ */
+ public void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
+
+ }
+
+ /**
+ * This method will be triggered by the platform after
+ * {@link #onSmsStatusReportReceived(int, int, byte[])} has been called to provide the result to
+ * the IMS provider.
+ *
+ * @param result result of delivering the message. Valid values are defined in
+ * {@link StatusReportResult}
+ * @param messageRef the message reference or -1 of unavailable.
+ */
+ public void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
+
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when there is an incoming message. The
+ * platform will deliver the message to the messages database and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSms(int, int)}.
+ *
+ * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
+ *
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDUs representing the contents of the message.
+ * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
+ */
+ public final void onSmsReceived(String format, byte[] pdu) throws IllegalStateException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsReceived(format, pdu);
+ acknowledgeSms(-1, DELIVER_STATUS_OK);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+ acknowledgeSms(-1, DELIVER_STATUS_ERROR);
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers to pass the result of the sent message
+ * to the platform.
+ *
+ * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
+ *
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult}
+ * @param reason reason in case status is failure. Valid values are:
+ * {@link SmsManager#RESULT_ERROR_NONE},
+ * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
+ * {@link SmsManager#RESULT_ERROR_NULL_PDU},
+ * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
+ * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED}
+ * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
+ * @throws RemoteException if the connection to the framework is not available. If this happens
+ * attempting to send the SMS should be aborted.
+ */
+ public final void onSendSmsResult(int messageRef, @SendStatusResult int status, int reason)
+ throws IllegalStateException, RemoteException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Feature not ready.");
+ }
+ mListener.onSendSmsResult(messageRef, status, reason);
+ }
+ }
+
+ /**
+ * Sets the status report of the sent message.
+ *
+ * @param messageRef the message reference.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDUs representing the content of the status report.
+ * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
+ */
+ public final void onSmsStatusReportReceived(int messageRef, String format, byte[] pdu) {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsStatusReportReceived(messageRef, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ acknowledgeSmsReport(messageRef, STATUS_REPORT_STATUS_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
+ * Provider.
+ *
+ * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ */
+ public String getSmsFormat() {
+ return SmsMessage.FORMAT_3GPP;
+ }
+
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
index 7125781..d976686 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
@@ -18,6 +18,7 @@
import android.os.Message;
import android.telephony.ims.internal.aidl.IImsMmTelListener;
+import android.telephony.ims.internal.aidl.IImsSmsListener;
import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
import android.telephony.ims.internal.aidl.IImsCallSessionListener;
import android.telephony.ims.internal.feature.CapabilityChangeRequest;
@@ -30,7 +31,7 @@
import com.android.ims.internal.IImsUt;
/**
- * See MmTelFeature for more information.
+ * See SmsImplBase for more information.
* {@hide}
*/
interface IImsMmTelFeature {
@@ -49,4 +50,10 @@
IImsCapabilityCallback c);
oneway void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c);
+ // SMS APIs
+ void setSmsListener(IImsSmsListener l);
+ oneway void sendSms(int messageRef, String format, String smsc, boolean retry, in byte[] pdu);
+ oneway void acknowledgeSms(int messageRef, int result);
+ oneway void acknowledgeSmsReport(int messageRef, int result);
+ String getSmsFormat();
}
diff --git a/telephony/java/com/android/ims/internal/ISmsListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
similarity index 65%
rename from telephony/java/com/android/ims/internal/ISmsListener.aidl
rename to telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
index 1266f04..468629a 100644
--- a/telephony/java/com/android/ims/internal/ISmsListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.ims.internal;
+package android.telephony.ims.internal.aidl;
/**
- * See SmsFeature for more information.
+ * See MMTelFeature for more information.
* {@hide}
*/
-interface ISmsListener {
- void setSentSmsResult(in int messageRef, in int result);
- void setSentSmsStatusReport(in int format, in byte[] pdu);
- void deliverSms(in int format, in byte[] pdu);
+interface IImsSmsListener {
+ void onSendSmsResult(in int messageRef, in int status, in int reason);
+ void onSmsStatusReportReceived(in int messageRef, in String format, in byte[] pdu);
+ void onSmsReceived(in String format, in byte[] pdu);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
index f183a57..2f350c8 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -21,11 +21,15 @@
import android.os.RemoteException;
import android.telecom.TelecomManager;
import android.telephony.ims.internal.ImsCallSessionListener;
+import android.telephony.ims.internal.SmsImplBase;
+import android.telephony.ims.internal.SmsImplBase.DeliverStatusResult;
+import android.telephony.ims.internal.SmsImplBase.StatusReportResult;
import android.telephony.ims.internal.aidl.IImsCallSessionListener;
import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsMmTelListener;
import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.internal.aidl.IImsSmsListener;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
@@ -64,6 +68,11 @@
}
@Override
+ public void setSmsListener(IImsSmsListener l) throws RemoteException {
+ MmTelFeature.this.setSmsListener(l);
+ }
+
+ @Override
public int getFeatureState() throws RemoteException {
synchronized (mLock) {
return MmTelFeature.this.getFeatureState();
@@ -143,6 +152,34 @@
IImsCapabilityCallback c) {
queryCapabilityConfigurationInternal(capability, radioTech, c);
}
+
+ @Override
+ public void sendSms(int messageRef, String format, String smsc, boolean retry, byte[] pdu) {
+ synchronized (mLock) {
+ MmTelFeature.this.sendSms(messageRef, format, smsc, retry, pdu);
+ }
+ }
+
+ @Override
+ public void acknowledgeSms(int messageRef, int result) {
+ synchronized (mLock) {
+ MmTelFeature.this.acknowledgeSms(messageRef, result);
+ }
+ }
+
+ @Override
+ public void acknowledgeSmsReport(int messageRef, int result) {
+ synchronized (mLock) {
+ MmTelFeature.this.acknowledgeSmsReport(messageRef, result);
+ }
+ }
+
+ @Override
+ public String getSmsFormat() {
+ synchronized (mLock) {
+ return MmTelFeature.this.getSmsFormat();
+ }
+ }
};
/**
@@ -171,7 +208,8 @@
value = {
CAPABILITY_TYPE_VOICE,
CAPABILITY_TYPE_VIDEO,
- CAPABILITY_TYPE_UT
+ CAPABILITY_TYPE_UT,
+ CAPABILITY_TYPE_SMS
})
@Retention(RetentionPolicy.SOURCE)
public @interface MmTelCapability {}
@@ -191,6 +229,11 @@
*/
public static final int CAPABILITY_TYPE_UT = 1 << 2;
+ /**
+ * This MmTelFeature supports SMS (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+
@Override
public final void addCapabilities(@MmTelCapability int capabilities) {
super.addCapabilities(capabilities);
@@ -239,6 +282,10 @@
}
}
+ private void setSmsListener(IImsSmsListener listener) {
+ getSmsImplementation().registerSmsListener(listener);
+ }
+
private void queryCapabilityConfigurationInternal(int capability, int radioTech,
IImsCapabilityCallback c) {
boolean enabled = queryCapabilityConfiguration(capability, radioTech);
@@ -400,6 +447,32 @@
// Base Implementation - Should be overridden
}
+ private void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
+ getSmsImplementation().sendSms(messageRef, format, smsc, isRetry, pdu);
+ }
+
+ private void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
+ getSmsImplementation().acknowledgeSms(messageRef, result);
+ }
+
+ private void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
+ getSmsImplementation().acknowledgeSmsReport(messageRef, result);
+ }
+
+ private String getSmsFormat() {
+ return getSmsImplementation().getSmsFormat();
+ }
+
+ /**
+ * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
+ * non-functional implementation is returned.
+ *
+ * @return an instance of {@link SmsImplBase} which should be implemented by the IMS Provider.
+ */
+ protected SmsImplBase getSmsImplementation() {
+ return new SmsImplBase();
+ }
+
/**{@inheritDoc}*/
@Override
public void onFeatureRemoved() {
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/ims/internal/IImsSmsFeature.aidl b/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl
deleted file mode 100644
index 5068128..0000000
--- a/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims.internal;
-
-import com.android.ims.internal.ISmsListener;
-
-/**
- * See SmsFeature for more information.
- *
- * {@hide}
- */
-interface IImsSmsFeature {
- void registerSmsListener(in ISmsListener listener);
- void sendSms(in int format, in int messageRef, in boolean retry, in byte[] pdu);
- void acknowledgeSms(in int messageRef, in int result);
- int getSmsFormat();
-}
\ No newline at end of file
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 + ")";
+ }
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f38a9a3..5d1e10e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -475,4 +475,26 @@
testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
udpEncapResp.fileDescriptor.close();
}
+
+ @Test
+ public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
+ IpSecUdpEncapResponse udpEncapResp =
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+ FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
+ ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+ try {
+ StructStat sockStat = Os.fstat(sockFd);
+ StructStat argStat = Os.fstat(arg);
+
+ return sockStat.st_ino == argStat.st_ino
+ && sockStat.st_dev == argStat.st_dev;
+ } catch (ErrnoException e) {
+ return false;
+ }
+ };
+
+ verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
+ mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
+ }
}
diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk
index 0e36981..a58ce78 100644
--- a/tests/testables/Android.mk
+++ b/tests/testables/Android.mk
@@ -25,10 +25,9 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- mockito-target-minus-junit4 \
legacy-android-test
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner mockito-target-minus-junit4
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index 43d1e37..700b7cb 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -25,9 +25,10 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- legacy-android-test \
- mockito-target-minus-junit4
+ legacy-android-test
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner \
+ mockito-target-minus-junit4
include $(BUILD_STATIC_JAVA_LIBRARY)