Merge "Add permission whitelist for EmergencyInfo app."
diff --git a/Android.bp b/Android.bp
index baf8e4f..254c5fc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -478,7 +478,8 @@
"telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
- "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
+ "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
+ "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
"telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
@@ -737,13 +738,17 @@
"core/tests/utiltests/jni",
]
+// TODO(b/77285514): remove this once the last few hidl interfaces have been
+// updated to use hwbinder.stubs.
java_library {
name: "hwbinder",
no_framework_libs: true,
srcs: [
"core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/IntDef.java",
"core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/SystemApi.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
"core/java/android/os/HwParcel.java",
@@ -955,3 +960,58 @@
exact_api_filename: "test-exact.txt",
args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.TestApi -nodocs",
}
+
+droiddoc {
+ name: "hwbinder-stubs-docs",
+ srcs: [
+ "core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/SystemApi.java",
+ "core/java/android/os/HwBinder.java",
+ "core/java/android/os/HwBlob.java",
+ "core/java/android/os/HwParcel.java",
+ "core/java/android/os/IHwBinder.java",
+ "core/java/android/os/IHwInterface.java",
+ "core/java/android/os/DeadObjectException.java",
+ "core/java/android/os/DeadSystemException.java",
+ "core/java/android/os/RemoteException.java",
+ "core/java/android/util/AndroidException.java",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ installable: false,
+ no_framework_libs: true,
+ args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly",
+}
+
+java_library_static {
+ name: "hwbinder.stubs",
+ sdk_version: "core_current",
+ srcs: [
+ ":hwbinder-stubs-docs",
+ ],
+}
+
+filegroup {
+ name: "apache-http-stubs-sources",
+ srcs: [
+ "core/java/org/apache/http/conn/ConnectTimeoutException.java",
+ "core/java/org/apache/http/conn/scheme/HostNameResolver.java",
+ "core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java",
+ "core/java/org/apache/http/conn/scheme/SocketFactory.java",
+ "core/java/org/apache/http/conn/ssl/AbstractVerifier.java",
+ "core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java",
+ "core/java/org/apache/http/conn/ssl/AndroidDistinguishedNameParser.java",
+ "core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java",
+ "core/java/org/apache/http/conn/ssl/SSLSocketFactory.java",
+ "core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java",
+ "core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java",
+ "core/java/org/apache/http/params/CoreConnectionPNames.java",
+ "core/java/org/apache/http/params/HttpConnectionParams.java",
+ "core/java/org/apache/http/params/HttpParams.java",
+ "core/java/android/net/http/HttpResponseCache.java",
+ "core/java/android/net/http/SslCertificate.java",
+ "core/java/android/net/http/SslError.java",
+ "core/java/com/android/internal/util/HexDump.java",
+ ],
+}
diff --git a/api/current.txt b/api/current.txt
index 603b6d4..7f076cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25828,8 +25828,8 @@
public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
method public void close() throws java.io.IOException;
+ method public java.io.FileDescriptor getFileDescriptor();
method public int getPort();
- method public java.io.FileDescriptor getSocket();
}
public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -37011,17 +37011,17 @@
package android.se.omapi {
- public class Channel {
+ public final class Channel implements java.nio.channels.Channel {
method public void close();
method public byte[] getSelectResponse();
method public android.se.omapi.Session getSession();
method public boolean isBasicChannel();
- method public boolean isClosed();
+ method public boolean isOpen();
method public boolean selectNext() throws java.io.IOException;
method public byte[] transmit(byte[]) throws java.io.IOException;
}
- public class Reader {
+ public final class Reader {
method public void closeSessions();
method public java.lang.String getName();
method public android.se.omapi.SEService getSEService();
@@ -37029,21 +37029,19 @@
method public android.se.omapi.Session openSession() throws java.io.IOException;
}
- public class SEService {
- ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
+ public final class SEService {
+ ctor public SEService(android.content.Context, java.util.concurrent.Executor, android.se.omapi.SEService.OnConnectedListener);
method public android.se.omapi.Reader[] getReaders();
method public java.lang.String getVersion();
method public boolean isConnected();
method public void shutdown();
}
- public static abstract class SEService.SecureElementListener extends android.os.Binder {
- ctor public SEService.SecureElementListener();
- method public android.os.IBinder asBinder();
- method public void serviceConnected();
+ public static abstract interface SEService.OnConnectedListener {
+ method public abstract void onConnected();
}
- public class Session {
+ public final class Session {
method public void close();
method public void closeChannels();
method public byte[] getATR();
@@ -39394,7 +39392,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
- method public void requestBluetoothAudio(java.lang.String);
+ method public void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void sendRemoteRttRequest();
method public final void sendRttInitiationFailure(int);
@@ -39605,7 +39603,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
- method public final void requestBluetoothAudio(java.lang.String);
+ method public final void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -40197,6 +40195,8 @@
public abstract class CellIdentity implements android.os.Parcelable {
method public int describeContents();
+ method public java.lang.CharSequence getOperatorAlphaLong();
+ method public java.lang.CharSequence getOperatorAlphaShort();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
}
@@ -40206,8 +40206,6 @@
method public int getLatitude();
method public int getLongitude();
method public int getNetworkId();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getSystemId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
@@ -40223,8 +40221,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public deprecated int getPsc();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
@@ -40239,8 +40235,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getPci();
method public int getTac();
method public void writeToParcel(android.os.Parcel, int);
@@ -40265,8 +40259,6 @@
method public deprecated int getMnc();
method public java.lang.String getMncString();
method public java.lang.String getMobileNetworkOperator();
- method public java.lang.CharSequence getOperatorAlphaLong();
- method public java.lang.CharSequence getOperatorAlphaShort();
method public int getPsc();
method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
@@ -40396,19 +40388,21 @@
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
- method public int cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public void addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener);
+ method public void addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener);
+ method public void cancelDownload(android.telephony.mbms.DownloadRequest);
method public void close();
method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
- method public int download(android.telephony.mbms.DownloadRequest);
+ method public void download(android.telephony.mbms.DownloadRequest);
method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
- method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback);
+ method public void removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener);
+ method public void removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener);
method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
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 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";
@@ -40872,12 +40866,11 @@
method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataSupported();
- method public deprecated boolean isDataEnabled();
+ method public boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
method public deprecated boolean isTtyModeSupported();
- method public boolean isUserMobileDataEnabled();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40888,13 +40881,12 @@
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 deprecated void setDataEnabled(boolean);
+ 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 setUserMobileDataEnabled(boolean);
method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -41065,21 +41057,21 @@
public class ApnSetting implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String getApnName();
+ method public int getApnTypeBitmask();
method public int getAuthType();
method public java.lang.String getEntryName();
method public int getId();
- method public int getMmsPort();
- method public java.net.InetAddress getMmsProxy();
- method public java.net.URL getMmsc();
- method public java.lang.String getMvnoType();
+ method public java.net.InetAddress getMmsProxyAddress();
+ method public int getMmsProxyPort();
+ method public android.net.Uri getMmsc();
+ method public int getMvnoType();
method public int getNetworkTypeBitmask();
method public java.lang.String getOperatorNumeric();
method public java.lang.String getPassword();
- method public int getPort();
- method public java.lang.String getProtocol();
- method public java.net.InetAddress getProxy();
- method public java.lang.String getRoamingProtocol();
- method public java.util.List<java.lang.String> getTypes();
+ method public int getProtocol();
+ method public java.net.InetAddress getProxyAddress();
+ method public int getProxyPort();
+ method public int getRoamingProtocol();
method public java.lang.String getUser();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
@@ -41088,46 +41080,45 @@
field public static final int AUTH_TYPE_PAP = 1; // 0x1
field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
- field public static final java.lang.String MVNO_TYPE_GID = "gid";
- field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
- field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
- field public static final java.lang.String MVNO_TYPE_SPN = "spn";
- field public static final java.lang.String PROTOCOL_IP = "IP";
- field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
- field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
- field public static final java.lang.String PROTOCOL_PPP = "PPP";
- field public static final java.lang.String TYPE_ALL = "*";
- field public static final java.lang.String TYPE_CBS = "cbs";
- field public static final java.lang.String TYPE_DEFAULT = "default";
- field public static final java.lang.String TYPE_DUN = "dun";
- field public static final java.lang.String TYPE_EMERGENCY = "emergency";
- field public static final java.lang.String TYPE_FOTA = "fota";
- field public static final java.lang.String TYPE_HIPRI = "hipri";
- field public static final java.lang.String TYPE_IA = "ia";
- field public static final java.lang.String TYPE_IMS = "ims";
- field public static final java.lang.String TYPE_MMS = "mms";
- field public static final java.lang.String TYPE_SUPL = "supl";
+ field public static final int MVNO_TYPE_GID = 2; // 0x2
+ field public static final int MVNO_TYPE_ICCID = 3; // 0x3
+ field public static final int MVNO_TYPE_IMSI = 1; // 0x1
+ field public static final int MVNO_TYPE_SPN = 0; // 0x0
+ field public static final int PROTOCOL_IP = 0; // 0x0
+ field public static final int PROTOCOL_IPV4V6 = 2; // 0x2
+ field public static final int PROTOCOL_IPV6 = 1; // 0x1
+ field public static final int PROTOCOL_PPP = 3; // 0x3
+ field public static final int TYPE_CBS = 128; // 0x80
+ field public static final int TYPE_DEFAULT = 17; // 0x11
+ field public static final int TYPE_DUN = 8; // 0x8
+ field public static final int TYPE_EMERGENCY = 512; // 0x200
+ field public static final int TYPE_FOTA = 32; // 0x20
+ field public static final int TYPE_HIPRI = 16; // 0x10
+ field public static final int TYPE_IA = 256; // 0x100
+ field public static final int TYPE_IMS = 64; // 0x40
+ field public static final int TYPE_MMS = 2; // 0x2
+ field public static final int TYPE_SUPL = 4; // 0x4
}
public static class ApnSetting.Builder {
ctor public ApnSetting.Builder();
method public android.telephony.data.ApnSetting build();
method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
method public android.telephony.data.ApnSetting.Builder setAuthType(int);
method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
- method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
- method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
- method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
- method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
+ method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri);
+ method public android.telephony.data.ApnSetting.Builder setMvnoType(int);
method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
- method public android.telephony.data.ApnSetting.Builder setPort(int);
- method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
- method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
- method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
- method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+ method public android.telephony.data.ApnSetting.Builder setProtocol(int);
+ method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setProxyPort(int);
+ method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int);
method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
}
@@ -41166,6 +41157,7 @@
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+ field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
}
@@ -41263,6 +41255,11 @@
package android.telephony.mbms {
+ public class DownloadProgressListener {
+ ctor public DownloadProgressListener();
+ method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
+ }
+
public final class DownloadRequest implements android.os.Parcelable {
method public int describeContents();
method public android.net.Uri getDestinationUri();
@@ -41286,15 +41283,9 @@
method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
}
- public class DownloadStateCallback {
- ctor public DownloadStateCallback();
- ctor public DownloadStateCallback(int);
- method public final boolean isFilterFlagSet(int);
- method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
- method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
- field public static final int ALL_UPDATES = 0; // 0x0
- field public static final int PROGRESS_UPDATES = 1; // 0x1
- field public static final int STATE_UPDATES = 2; // 0x2
+ public class DownloadStatusListener {
+ ctor public DownloadStatusListener();
+ method public void onStatusUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
}
public final class FileInfo implements android.os.Parcelable {
@@ -41329,6 +41320,7 @@
field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0
+ field public static final int UNKNOWN = -1; // 0xffffffff
}
public static class MbmsErrors.DownloadErrors {
diff --git a/api/system-current.txt b/api/system-current.txt
index 6325de7..d6651d9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3161,6 +3161,135 @@
field public static final java.lang.String ACTION_UPDATE_TZDATA = "android.intent.action.UPDATE_TZDATA";
}
+ public class HidlSupport {
+ method public static boolean deepEquals(java.lang.Object, java.lang.Object);
+ method public static int deepHashCode(java.lang.Object);
+ method public static int getPidIfSharable();
+ method public static boolean interfacesEqual(android.os.IHwInterface, java.lang.Object);
+ }
+
+ public abstract class HwBinder implements android.os.IHwBinder {
+ ctor public HwBinder();
+ method public static final void configureRpcThreadpool(long, boolean);
+ method public static void enableInstrumentation();
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final void joinRpcThreadpool();
+ method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public final void registerService(java.lang.String) throws android.os.RemoteException;
+ method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ }
+
+ public class HwBlob {
+ ctor public HwBlob(int);
+ method public final void copyToBoolArray(long, boolean[], int);
+ method public final void copyToDoubleArray(long, double[], int);
+ method public final void copyToFloatArray(long, float[], int);
+ method public final void copyToInt16Array(long, short[], int);
+ method public final void copyToInt32Array(long, int[], int);
+ method public final void copyToInt64Array(long, long[], int);
+ method public final void copyToInt8Array(long, byte[], int);
+ method public final boolean getBool(long);
+ method public final double getDouble(long);
+ method public final float getFloat(long);
+ method public final short getInt16(long);
+ method public final int getInt32(long);
+ method public final long getInt64(long);
+ method public final byte getInt8(long);
+ method public final java.lang.String getString(long);
+ method public final long handle();
+ method public final void putBlob(long, android.os.HwBlob);
+ method public final void putBool(long, boolean);
+ method public final void putBoolArray(long, boolean[]);
+ method public final void putDouble(long, double);
+ method public final void putDoubleArray(long, double[]);
+ method public final void putFloat(long, float);
+ method public final void putFloatArray(long, float[]);
+ method public final void putInt16(long, short);
+ method public final void putInt16Array(long, short[]);
+ method public final void putInt32(long, int);
+ method public final void putInt32Array(long, int[]);
+ method public final void putInt64(long, long);
+ method public final void putInt64Array(long, long[]);
+ method public final void putInt8(long, byte);
+ method public final void putInt8Array(long, byte[]);
+ method public final void putString(long, java.lang.String);
+ method public static java.lang.Boolean[] wrapArray(boolean[]);
+ method public static java.lang.Long[] wrapArray(long[]);
+ method public static java.lang.Byte[] wrapArray(byte[]);
+ method public static java.lang.Short[] wrapArray(short[]);
+ method public static java.lang.Integer[] wrapArray(int[]);
+ method public static java.lang.Float[] wrapArray(float[]);
+ method public static java.lang.Double[] wrapArray(double[]);
+ }
+
+ public class HwParcel {
+ ctor public HwParcel();
+ method public final void enforceInterface(java.lang.String);
+ method public final boolean readBool();
+ method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
+ method public final android.os.HwBlob readBuffer(long);
+ method public final double readDouble();
+ method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
+ method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+ method public final float readFloat();
+ method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+ method public final short readInt16();
+ method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
+ method public final int readInt32();
+ method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
+ method public final long readInt64();
+ method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
+ method public final byte readInt8();
+ method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
+ method public final java.lang.String readString();
+ method public final java.util.ArrayList<java.lang.String> readStringVector();
+ method public final android.os.IHwBinder readStrongBinder();
+ method public final void release();
+ method public final void releaseTemporaryStorage();
+ method public final void send();
+ method public final void verifySuccess();
+ method public final void writeBool(boolean);
+ method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
+ method public final void writeBuffer(android.os.HwBlob);
+ method public final void writeDouble(double);
+ method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
+ method public final void writeFloat(float);
+ method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+ method public final void writeInt16(short);
+ method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
+ method public final void writeInt32(int);
+ method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
+ method public final void writeInt64(long);
+ method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
+ method public final void writeInt8(byte);
+ method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
+ method public final void writeInterfaceToken(java.lang.String);
+ method public final void writeStatus(int);
+ method public final void writeString(java.lang.String);
+ method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
+ method public final void writeStrongBinder(android.os.IHwBinder);
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public static abstract class HwParcel.Status implements java.lang.annotation.Annotation {
+ }
+
+ public abstract interface IHwBinder {
+ method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
+ method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String);
+ method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
+ }
+
+ public static abstract interface IHwBinder.DeathRecipient {
+ method public abstract void serviceDied(long);
+ }
+
+ public abstract interface IHwInterface {
+ method public abstract android.os.IHwBinder asBinder();
+ }
+
public class IncidentManager {
method public void reportIncident(android.os.IncidentReportArgs);
method public void reportIncident(java.lang.String, byte[]);
@@ -4544,7 +4673,6 @@
field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
- field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
}
@@ -5347,18 +5475,20 @@
public class MbmsDownloadServiceBase extends android.os.Binder {
ctor public MbmsDownloadServiceBase();
+ method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
- method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
- method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
}
public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/api/test-current.txt b/api/test-current.txt
index 05f5c77..709b37e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -440,10 +440,20 @@
method public void setCdmaSystemAndNetworkId(int, int);
}
+ public class TelephonyManager {
+ method public int getCarrierIdListVersion();
+ method public void setCarrierTestOverride(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
+ }
+
}
package android.telephony.mbms {
+ public static class DownloadRequest.Builder {
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
+ }
+
public final class FileInfo implements android.os.Parcelable {
ctor public FileInfo(android.net.Uri, java.lang.String);
}
@@ -470,18 +480,20 @@
public class MbmsDownloadServiceBase extends android.os.Binder {
ctor public MbmsDownloadServiceBase();
+ method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
- method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
- method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
}
public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2eabd86..44a8930 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -264,7 +264,7 @@
* @return Whether the dialog is currently showing.
*/
public boolean isShowing() {
- return mShowing;
+ return mDecor == null ? false : mDecor.getVisibility() == View.VISIBLE;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 3df4336..71edc8a 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1514,22 +1514,24 @@
* @return true, if the request is send to the Bluetooth stack.
* @hide
*/
- public boolean requestLeConnectionUpdate(int minConnectionInterval,
- int maxConnectionInterval,
- int slaveLatency, int supervisionTimeout) {
+ public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
+ int slaveLatency, int supervisionTimeout,
+ int minConnectionEventLen, int maxConnectionEventLen) {
if (DBG) {
Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
- + ")" + (1.25 * minConnectionInterval)
- + "msec, max=(" + maxConnectionInterval + ")"
+ + ")" + (1.25 * minConnectionInterval)
+ + "msec, max=(" + maxConnectionInterval + ")"
+ (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
- + ", timeout=" + supervisionTimeout + "msec");
+ + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
+ + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
}
if (mService == null || mClientIf == 0) return false;
try {
mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
- minConnectionInterval, maxConnectionInterval,
- slaveLatency, supervisionTimeout);
+ minConnectionInterval, maxConnectionInterval,
+ slaveLatency, supervisionTimeout,
+ minConnectionEventLen, maxConnectionEventLen);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 647e0d0..159e165 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -379,6 +380,76 @@
}
/**
+ * Select a connected device as active.
+ *
+ * The active device selection is per profile. An active device's
+ * purpose is profile-specific. For example, Hearing Aid audio
+ * streaming is to the active Hearing Aid device. If a remote device
+ * is not connected, it cannot be selected as active.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is not connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that the
+ * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+ * with the active device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device the remote Bluetooth device. Could be null to clear
+ * the active device and stop streaming audio to a Bluetooth device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+ if (DBG) log("setActiveDevice(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && ((device == null) || isValidDevice(device))) {
+ mService.setActiveDevice(device);
+ return true;
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the connected physical Hearing Aid devices that are active
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * permission.
+ *
+ * @return the list of active devices. The first element is the left active
+ * device; the second element is the right active device. If either or both side
+ * is not active, it will be null on that position. Returns empty list on error.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public List<BluetoothDevice> getActiveDevices() {
+ if (VDBG) log("getActiveDevices()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getActiveDevices();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
* Set priority of the profile
*
* <p> The device should already be paired.
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index a3d6968..4a466c6 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -701,18 +701,17 @@
}
/**
- * Sends Virtual Cable Unplug to currently connected host.
+ * Gets the application name of the current HidDeviceService user.
*
- * @return
+ * @return the current user name, or empty string if cannot get the name
* {@hide}
*/
- public boolean unplug(BluetoothDevice device) {
- boolean result = false;
-
+ public String getUserAppName() {
final IBluetoothHidDevice service = mService;
+
if (service != null) {
try {
- result = service.unplug(device);
+ return service.getUserAppName();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -720,7 +719,7 @@
Log.w(TAG, "Proxy not attached to service");
}
- return result;
+ return "";
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9d518e9..40f47b0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -861,6 +861,10 @@
* You should always check {@link NetworkInfo#isConnected()} before initiating
* network traffic. This may return {@code null} when there is no default
* network.
+ * Note that if the default network is a VPN, this method will return the
+ * NetworkInfo for one of its underlying networks instead, or null if the
+ * VPN agent did not specify any. Apps interested in learning about VPNs
+ * should use {@link #getNetworkInfo(android.net.Network)} instead.
*
* @return a {@link NetworkInfo} object for the current default network
* or {@code null} if no default network is currently active
@@ -1018,7 +1022,11 @@
* which you're interested.
* @return a {@link NetworkInfo} object for the requested
* network type or {@code null} if the type is not
- * supported by the device.
+ * supported by the device. If {@code networkType} is
+ * TYPE_VPN and a VPN is active for the calling app,
+ * then this method will try to return one of the
+ * underlying networks for the VPN or null if the
+ * VPN agent didn't specify any.
*
* @deprecated This method does not support multiple connected networks
* of the same type. Use {@link #getAllNetworks} and
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 57f0588..8034bb6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -56,7 +56,8 @@
* new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
*
* <p>Keys for this algorithm must be 128 bits in length.
- * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
*/
public static final String AUTH_HMAC_MD5 = "hmac(md5)";
@@ -65,7 +66,8 @@
* new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
*
* <p>Keys for this algorithm must be 160 bits in length.
- * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
*/
public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
@@ -73,7 +75,8 @@
* SHA256 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 256 bits in length.
- * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
*/
public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -81,7 +84,8 @@
* SHA384 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 384 bits in length.
- * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
*/
public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
@@ -89,7 +93,8 @@
* SHA512 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 512 bits in length.
- * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
*/
public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
@@ -112,6 +117,7 @@
AUTH_HMAC_MD5,
AUTH_HMAC_SHA1,
AUTH_HMAC_SHA256,
+ AUTH_HMAC_SHA384,
AUTH_HMAC_SHA512,
AUTH_CRYPT_AES_GCM
})
@@ -126,11 +132,14 @@
* Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
* defined as constants in this class.
*
+ * <p>For algorithms that produce an integrity check value, the truncation length is a required
+ * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
+ *
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
*/
public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
- this(algorithm, key, key.length * 8);
+ this(algorithm, key, 0);
}
/**
@@ -228,6 +237,7 @@
case AUTH_CRYPT_AES_GCM:
// The keying material for GCM is a key plus a 32-bit salt
isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+ isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
break;
default:
throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 4157845..cc22771 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -274,7 +274,8 @@
*
* @param destinationAddress the destination address for traffic bearing the requested SPI.
* For inbound traffic, the destination should be an address currently assigned on-device.
- * @param requestedSpi the requested SPI, or '0' to allocate a random SPI
+ * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
+ * reserved and may not be used. See RFC 4303 Section 2.1.
* @return the reserved SecurityParameterIndex
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
* currently allocated for this user
@@ -305,6 +306,19 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
+ * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+ * applied transform before completion of graceful shutdown may result in the shutdown sequence
+ * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+ * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+ * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+ * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+ * sufficient to ensure shutdown.
+ *
+ * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+ * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+ * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+ * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+ *
* <h4>Rekey Procedure</h4>
*
* <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -322,6 +336,9 @@
*/
public void applyTransportModeTransform(@NonNull Socket socket,
@PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
+ // Ensure creation of FD. See b/77548890 for more details.
+ socket.getSoLinger();
+
applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
@@ -373,6 +390,19 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
+ * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+ * applied transform before completion of graceful shutdown may result in the shutdown sequence
+ * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+ * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+ * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+ * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+ * sufficient to ensure shutdown.
+ *
+ * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+ * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+ * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+ * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+ *
* <h4>Rekey Procedure</h4>
*
* <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -413,6 +443,9 @@
* @throws IOException indicating that the transform could not be removed from the socket
*/
public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
+ // Ensure creation of FD. See b/77548890 for more details.
+ socket.getSoLinger();
+
removeTransportModeTransforms(socket.getFileDescriptor$());
}
@@ -476,7 +509,7 @@
* signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
* IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
* caller. The caller should not close the {@code FileDescriptor} returned by {@link
- * #getSocket}, but should use {@link #close} instead.
+ * #getFileDescriptor}, but should use {@link #close} instead.
*
* <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
* of the next user who binds to that port. To prevent this scenario, these sockets are held
@@ -515,8 +548,8 @@
mCloseGuard.open("constructor");
}
- /** Get the wrapped socket. */
- public FileDescriptor getSocket() {
+ /** Get the encapsulation socket's file descriptor. */
+ public FileDescriptor getFileDescriptor() {
if (mPfd == null) {
return null;
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3683d34..f14847f 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -25,6 +25,8 @@
import com.android.okhttp.internalandroidapi.Dns;
import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramSocket;
@@ -136,9 +138,15 @@
for (int i = 0; i < hostAddresses.length; i++) {
try {
Socket socket = createSocket();
- if (localAddress != null) socket.bind(localAddress);
- socket.connect(new InetSocketAddress(hostAddresses[i], port));
- return socket;
+ boolean failed = true;
+ try {
+ if (localAddress != null) socket.bind(localAddress);
+ socket.connect(new InetSocketAddress(hostAddresses[i], port));
+ failed = false;
+ return socket;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
} catch (IOException e) {
if (i == (hostAddresses.length - 1)) throw e;
}
@@ -155,15 +163,27 @@
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = createSocket();
- socket.bind(new InetSocketAddress(localAddress, localPort));
- socket.connect(new InetSocketAddress(address, port));
+ boolean failed = true;
+ try {
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = createSocket();
- socket.connect(new InetSocketAddress(host, port));
+ boolean failed = true;
+ try {
+ socket.connect(new InetSocketAddress(host, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
@@ -175,7 +195,13 @@
@Override
public Socket createSocket() throws IOException {
Socket socket = new Socket();
- bindSocket(socket);
+ boolean failed = true;
+ try {
+ bindSocket(socket);
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 22cffcc..19f0c90 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -315,7 +315,7 @@
/**
* Capabilities that suggest that a network is restricted.
- * {@see #maybeMarkCapabilitiesRestricted}.
+ * {@see #maybeMarkCapabilitiesRestricted}, {@see #FORCE_RESTRICTED_CAPABILITIES}
*/
@VisibleForTesting
/* package */ static final long RESTRICTED_CAPABILITIES =
@@ -326,7 +326,13 @@
(1 << NET_CAPABILITY_IA) |
(1 << NET_CAPABILITY_IMS) |
(1 << NET_CAPABILITY_RCS) |
- (1 << NET_CAPABILITY_XCAP) |
+ (1 << NET_CAPABILITY_XCAP);
+
+ /**
+ * Capabilities that force network to be restricted.
+ * {@see #maybeMarkCapabilitiesRestricted}.
+ */
+ private static final long FORCE_RESTRICTED_CAPABILITIES =
(1 << NET_CAPABILITY_OEM_PAID);
/**
@@ -531,16 +537,21 @@
* @hide
*/
public void maybeMarkCapabilitiesRestricted() {
+ // Check if we have any capability that forces the network to be restricted.
+ final boolean forceRestrictedCapability =
+ (mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
+
// Verify there aren't any unrestricted capabilities. If there are we say
- // the whole thing is unrestricted.
+ // the whole thing is unrestricted unless it is forced to be restricted.
final boolean hasUnrestrictedCapabilities =
- ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0);
+ (mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0;
// Must have at least some restricted capabilities.
final boolean hasRestrictedCapabilities =
- ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0);
+ (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
- if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) {
+ if (forceRestrictedCapability
+ || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 940f985..d6cbc88 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -31,6 +31,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Map;
import java.util.Objects;
/**
@@ -97,6 +98,11 @@
/** Denotes a request for stats at the interface and UID level. */
public static final int STATS_PER_UID = 1;
+ private static final String CLATD_INTERFACE_PREFIX = "v4-";
+ // Delta between IPv4 header (20b) and IPv6 header (40b).
+ // Used for correct stats accounting on clatd interfaces.
+ private static final int IPV4V6_HEADER_DELTA = 20;
+
// TODO: move fields to "mVariable" notation
/**
@@ -752,6 +758,75 @@
}
/**
+ * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+ *
+ * <p>This mutates both base and stacked traffic stats, to account respectively for
+ * double-counted traffic and IPv4/IPv6 header size difference.
+ *
+ * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
+ * packet on the stacked interface, and once as translated to an IPv6 packet on the
+ * base interface. For correct stats accounting on the base interface, every 464xlat
+ * packet needs to be subtracted from the root UID on the base interface both for tx
+ * and rx traffic (http://b/12249687, http:/b/33681750).
+ *
+ * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
+ * {@code ConcurrentHashMap}
+ * @param baseTraffic Traffic on the base interfaces. Will be mutated.
+ * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
+ * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+ */
+ public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+ NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+ // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
+ // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
+ final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
+
+ // For recycling
+ Entry entry = null;
+ Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
+
+ for (int i = 0; i < stackedTraffic.size; i++) {
+ entry = stackedTraffic.getValues(i, entry);
+ if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
+ continue;
+ }
+ final String baseIface = stackedIfaces.get(entry.iface);
+ if (baseIface == null) {
+ continue;
+ }
+ // Subtract any 464lat traffic seen for the root UID on the current base interface.
+ adjust.iface = baseIface;
+ adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+ adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
+ adjust.rxPackets = -entry.rxPackets;
+ adjust.txPackets = -entry.txPackets;
+ adjustments.combineValues(adjust);
+
+ // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
+ // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+ // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
+ // difference for all packets (http://b/12249687, http:/b/33681750).
+ entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
+ entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
+ stackedTraffic.setValues(i, entry);
+ }
+
+ baseTraffic.combineAllValues(adjustments);
+ }
+
+ /**
+ * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+ *
+ * <p>This mutates the object this method is called on. Equivalent to calling
+ * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
+ * base and stacked traffic.
+ * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+ */
+ public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
+ apply464xlatAdjustments(this, this, stackedIfaces);
+ }
+
+ /**
* Return total statistics grouped by {@link #iface}; doesn't mutate the
* original structure.
*/
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index 3b0dc7e..76a781d 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
import android.os.Parcelable;
/**
- * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * An event logged for an interface with APF capabilities when its IpClient state machine exits.
* {@hide}
*/
public final class ApfStats implements Parcelable {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 3f98a88..d44d515 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -188,7 +188,11 @@
public static final String INCREMENTAL = getString("ro.build.version.incremental");
/**
- * The user-visible version string. E.g., "1.0" or "3.4b5".
+ * The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas".
+ *
+ * This field is an opaque string. Do not assume that its value
+ * has any particular structure or that values of RELEASE from
+ * different releases can be somehow ordered.
*/
public static final String RELEASE = getString("ro.build.version.release");
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 4d7d931..91b796a 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.SystemApi;
+
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -25,6 +27,7 @@
import java.util.stream.IntStream;
/** @hide */
+@SystemApi
public class HidlSupport {
/**
* Similar to Objects.deepEquals, but also take care of lists.
@@ -36,7 +39,9 @@
* 2.3 Both are Lists, elements are checked recursively
* 2.4 (If both are collections other than lists or maps, throw an error)
* 2.5 lft.equals(rgt) returns true
+ * @hide
*/
+ @SystemApi
public static boolean deepEquals(Object lft, Object rgt) {
if (lft == rgt) {
return true;
@@ -91,6 +96,7 @@
* and should be avoided).
*
* @param <E> Inner object type.
+ * @hide
*/
public static final class Mutable<E> {
public E value;
@@ -106,7 +112,9 @@
/**
* Similar to Arrays.deepHashCode, but also take care of lists.
+ * @hide
*/
+ @SystemApi
public static int deepHashCode(Object o) {
if (o == null) {
return 0;
@@ -133,6 +141,7 @@
return o.hashCode();
}
+ /** @hide */
private static void throwErrorIfUnsupportedType(Object o) {
if (o instanceof Collection<?> && !(o instanceof List<?>)) {
throw new UnsupportedOperationException(
@@ -146,6 +155,7 @@
}
}
+ /** @hide */
private static int primitiveArrayHashCode(Object o) {
Class<?> elementType = o.getClass().getComponentType();
if (elementType == boolean.class) {
@@ -185,7 +195,9 @@
* - If both interfaces are stubs, asBinder() returns the object itself. By default,
* auto-generated IFoo.Stub does not override equals(), but an implementation can
* optionally override it, and {@code interfacesEqual} will use it here.
+ * @hide
*/
+ @SystemApi
public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
if (lft == rgt) {
return true;
@@ -200,7 +212,12 @@
}
/**
- * Return PID of process if sharable to clients.
+ * Return PID of process only if on a non-user build. For debugging purposes.
+ * @hide
*/
+ @SystemApi
public static native int getPidIfSharable();
+
+ /** @hide */
+ public HidlSupport() {}
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 5e2a081..228fe7a 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,16 +16,26 @@
package android.os;
+import android.annotation.SystemApi;
+
import libcore.util.NativeAllocationRegistry;
import java.util.NoSuchElementException;
/** @hide */
+@SystemApi
public abstract class HwBinder implements IHwBinder {
private static final String TAG = "HwBinder";
private static final NativeAllocationRegistry sNativeRegistry;
+ /**
+ * Create and initialize a HwBinder object and the native objects
+ * used to allow this to participate in hwbinder transactions.
+ *
+ * @hide
+ */
+ @SystemApi
public HwBinder() {
native_setup();
@@ -34,33 +44,90 @@
mNativeContext);
}
+ /** @hide */
@Override
public final native void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Process a hwbinder transaction.
+ *
+ * @param code interface specific code for interface.
+ * @param request parceled transaction
+ * @param reply object to parcel reply into
+ * @param flags transaction flags to be chosen by wire protocol
+ *
+ * @hide
+ */
+ @SystemApi
public abstract void onTransact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Registers this service with the hwservicemanager.
+ *
+ * @param serviceName instance name of the service
+ * @hide
+ */
+ @SystemApi
public native final void registerService(String serviceName)
throws RemoteException;
+ /**
+ * Returns the specified service from the hwservicemanager. Does not retry.
+ *
+ * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
+ * @param serviceName the instance name of the service for example default.
+ * @throws NoSuchElementException when the service is unavailable
+ * @hide
+ */
+ @SystemApi
public static final IHwBinder getService(
String iface,
String serviceName)
throws RemoteException, NoSuchElementException {
return getService(iface, serviceName, false /* retry */);
}
+ /**
+ * Returns the specified service from the hwservicemanager.
+ * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
+ * @param serviceName the instance name of the service for example default.
+ * @param retry whether to wait for the service to start if it's not already started
+ * @throws NoSuchElementException when the service is unavailable
+ * @hide
+ */
+ @SystemApi
public static native final IHwBinder getService(
String iface,
String serviceName,
boolean retry)
throws RemoteException, NoSuchElementException;
+ /**
+ * Configures how many threads the process-wide hwbinder threadpool
+ * has to process incoming requests.
+ *
+ * @param maxThreads total number of threads to create (includes this thread if
+ * callerWillJoin is true)
+ * @param callerWillJoin whether joinRpcThreadpool will be called in advance
+ * @hide
+ */
+ @SystemApi
public static native final void configureRpcThreadpool(
long maxThreads, boolean callerWillJoin);
+ /**
+ * Current thread will join hwbinder threadpool and process
+ * commands in the pool. Should be called after configuring
+ * a threadpool with callerWillJoin true and then registering
+ * the provided service if this thread doesn't need to do
+ * anything else.
+ *
+ * @hide
+ */
+ @SystemApi
public static native final void joinRpcThreadpool();
// Returns address of the "freeFunction".
@@ -82,7 +149,26 @@
private static native void native_report_sysprop_change();
/**
+ * Enable instrumentation if available.
+ *
+ * On a non-user build, this method:
+ * - tries to enable atracing (if enabled)
+ * - tries to enable coverage dumps (if running in VTS)
+ * - tries to enable record and replay (if running in VTS)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static void enableInstrumentation() {
+ native_report_sysprop_change();
+ }
+
+ /**
* Notifies listeners that a system property has changed
+ *
+ * TODO(b/72480743): remove this method
+ *
+ * @hide
*/
public static void reportSyspropChanged() {
native_report_sysprop_change();
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 5e9b9ae3..405651e 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -17,10 +17,17 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import libcore.util.NativeAllocationRegistry;
-/** @hide */
+/**
+ * Represents fixed sized allocation of marshalled data used. Helper methods
+ * allow for access to the unmarshalled data in a variety of ways.
+ *
+ * @hide
+ */
+@SystemApi
public class HwBlob {
private static final String TAG = "HwBlob";
@@ -34,48 +41,276 @@
mNativeContext);
}
+ /**
+ * @param offset offset to unmarshall a boolean from
+ * @return the unmarshalled boolean value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final boolean getBool(long offset);
+ /**
+ * @param offset offset to unmarshall a byte from
+ * @return the unmarshalled byte value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final byte getInt8(long offset);
+ /**
+ * @param offset offset to unmarshall a short from
+ * @return the unmarshalled short value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final short getInt16(long offset);
+ /**
+ * @param offset offset to unmarshall an int from
+ * @return the unmarshalled int value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final int getInt32(long offset);
+ /**
+ * @param offset offset to unmarshall a long from
+ * @return the unmarshalled long value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final long getInt64(long offset);
+ /**
+ * @param offset offset to unmarshall a float from
+ * @return the unmarshalled float value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final float getFloat(long offset);
+ /**
+ * @param offset offset to unmarshall a double from
+ * @return the unmarshalled double value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final double getDouble(long offset);
+ /**
+ * @param offset offset to unmarshall a string from
+ * @return the unmarshalled string value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final String getString(long offset);
/**
- The copyTo... methods copy the blob's data, starting from the given
- byte offset, into the array. A total of "size" _elements_ are copied.
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
*/
public native final void copyToBoolArray(long offset, boolean[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+ */
public native final void copyToInt8Array(long offset, byte[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+ */
public native final void copyToInt16Array(long offset, short[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+ */
public native final void copyToInt32Array(long offset, int[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+ */
public native final void copyToInt64Array(long offset, long[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+ */
public native final void copyToFloatArray(long offset, float[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+ */
public native final void copyToDoubleArray(long offset, double[] array, int size);
+ /**
+ * Writes a boolean value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jboolean)] is out of range
+ */
public native final void putBool(long offset, boolean x);
+ /**
+ * Writes a byte value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jbyte)] is out of range
+ */
public native final void putInt8(long offset, byte x);
+ /**
+ * Writes a short value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jshort)] is out of range
+ */
public native final void putInt16(long offset, short x);
+ /**
+ * Writes a int value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jint)] is out of range
+ */
public native final void putInt32(long offset, int x);
+ /**
+ * Writes a long value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jlong)] is out of range
+ */
public native final void putInt64(long offset, long x);
+ /**
+ * Writes a float value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jfloat)] is out of range
+ */
public native final void putFloat(long offset, float x);
+ /**
+ * Writes a double value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jdouble)] is out of range
+ */
public native final void putDouble(long offset, double x);
+ /**
+ * Writes a string value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jstring)] is out of range
+ */
public native final void putString(long offset, String x);
+ /**
+ * Put a boolean array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
+ */
public native final void putBoolArray(long offset, boolean[] x);
+ /**
+ * Put a byte array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+ */
public native final void putInt8Array(long offset, byte[] x);
+ /**
+ * Put a short array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+ */
public native final void putInt16Array(long offset, short[] x);
+ /**
+ * Put a int array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+ */
public native final void putInt32Array(long offset, int[] x);
+ /**
+ * Put a long array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+ */
public native final void putInt64Array(long offset, long[] x);
+ /**
+ * Put a float array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+ */
public native final void putFloatArray(long offset, float[] x);
+ /**
+ * Put a double array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+ */
public native final void putDoubleArray(long offset, double[] x);
+ /**
+ * Write another HwBlob into this blob at the specified location.
+ *
+ * @param offset location to write value
+ * @param blob data to write
+ * @throws IndexOutOfBoundsException if [offset, offset + blob's size] outside of the range of
+ * this blob.
+ */
public native final void putBlob(long offset, HwBlob blob);
+ /**
+ * @return current handle of HwBlob for reference in a parcelled binder transaction
+ */
public native final long handle();
+ /**
+ * Convert a primitive to a wrapped array for boolean.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Boolean[] wrapArray(@NonNull boolean[] array) {
final int n = array.length;
Boolean[] wrappedArray = new Boolean[n];
@@ -85,6 +320,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for long.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Long[] wrapArray(@NonNull long[] array) {
final int n = array.length;
Long[] wrappedArray = new Long[n];
@@ -94,6 +335,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for byte.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Byte[] wrapArray(@NonNull byte[] array) {
final int n = array.length;
Byte[] wrappedArray = new Byte[n];
@@ -103,6 +350,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for short.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Short[] wrapArray(@NonNull short[] array) {
final int n = array.length;
Short[] wrappedArray = new Short[n];
@@ -112,6 +365,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for int.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Integer[] wrapArray(@NonNull int[] array) {
final int n = array.length;
Integer[] wrappedArray = new Integer[n];
@@ -121,6 +380,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for float.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Float[] wrapArray(@NonNull float[] array) {
final int n = array.length;
Float[] wrappedArray = new Float[n];
@@ -130,6 +395,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for double.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Double[] wrapArray(@NonNull double[] array) {
final int n = array.length;
Double[] wrappedArray = new Double[n];
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 4ba1144..0eb62c95 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -16,17 +16,32 @@
package android.os;
-import java.util.ArrayList;
-import java.util.Arrays;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/** @hide */
+@SystemApi
public class HwParcel {
private static final String TAG = "HwParcel";
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_SUCCESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {}
+
+ /**
+ * Success return error for a transaction. Written to parcels
+ * using writeStatus.
+ */
public static final int STATUS_SUCCESS = 0;
- public static final int STATUS_ERROR = -1;
private static final NativeAllocationRegistry sNativeRegistry;
@@ -38,6 +53,9 @@
mNativeContext);
}
+ /**
+ * Creates an initialized and empty parcel.
+ */
public HwParcel() {
native_setup(true /* allocate */);
@@ -46,25 +64,106 @@
mNativeContext);
}
+ /**
+ * Writes an interface token into the parcel used to verify that
+ * a transaction has made it to the write type of interface.
+ *
+ * @param interfaceName fully qualified name of interface message
+ * is being sent to.
+ */
public native final void writeInterfaceToken(String interfaceName);
+ /**
+ * Writes a boolean value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeBool(boolean val);
+ /**
+ * Writes a byte value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt8(byte val);
+ /**
+ * Writes a short value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt16(short val);
+ /**
+ * Writes a int value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt32(int val);
+ /**
+ * Writes a long value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt64(long val);
+ /**
+ * Writes a float value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeFloat(float val);
+ /**
+ * Writes a double value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeDouble(double val);
+ /**
+ * Writes a String value to the end of the parcel.
+ *
+ * Note, this will be converted to UTF-8 when it is written.
+ *
+ * @param val to write
+ */
public native final void writeString(String val);
+ /**
+ * Writes an array of boolean values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeBoolVector(boolean[] val);
+ /**
+ * Writes an array of byte values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt8Vector(byte[] val);
+ /**
+ * Writes an array of short values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt16Vector(short[] val);
+ /**
+ * Writes an array of int values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt32Vector(int[] val);
+ /**
+ * Writes an array of long values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt64Vector(long[] val);
+ /**
+ * Writes an array of float values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeFloatVector(float[] val);
+ /**
+ * Writes an array of double values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeDoubleVector(double[] val);
+ /**
+ * Writes an array of String values to the end of the parcel.
+ *
+ * Note, these will be converted to UTF-8 as they are written.
+ *
+ * @param val to write
+ */
private native final void writeStringVector(String[] val);
+ /**
+ * Helper method to write a list of Booleans to val.
+ * @param val list to write
+ */
public final void writeBoolVector(ArrayList<Boolean> val) {
final int n = val.size();
boolean[] array = new boolean[n];
@@ -75,6 +174,10 @@
writeBoolVector(array);
}
+ /**
+ * Helper method to write a list of Booleans to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt8Vector(ArrayList<Byte> val) {
final int n = val.size();
byte[] array = new byte[n];
@@ -85,6 +188,10 @@
writeInt8Vector(array);
}
+ /**
+ * Helper method to write a list of Shorts to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt16Vector(ArrayList<Short> val) {
final int n = val.size();
short[] array = new short[n];
@@ -95,6 +202,10 @@
writeInt16Vector(array);
}
+ /**
+ * Helper method to write a list of Integers to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt32Vector(ArrayList<Integer> val) {
final int n = val.size();
int[] array = new int[n];
@@ -105,6 +216,10 @@
writeInt32Vector(array);
}
+ /**
+ * Helper method to write a list of Longs to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt64Vector(ArrayList<Long> val) {
final int n = val.size();
long[] array = new long[n];
@@ -115,6 +230,10 @@
writeInt64Vector(array);
}
+ /**
+ * Helper method to write a list of Floats to the end of the parcel.
+ * @param val list to write
+ */
public final void writeFloatVector(ArrayList<Float> val) {
final int n = val.size();
float[] array = new float[n];
@@ -125,6 +244,10 @@
writeFloatVector(array);
}
+ /**
+ * Helper method to write a list of Doubles to the end of the parcel.
+ * @param val list to write
+ */
public final void writeDoubleVector(ArrayList<Double> val) {
final int n = val.size();
double[] array = new double[n];
@@ -135,93 +258,272 @@
writeDoubleVector(array);
}
+ /**
+ * Helper method to write a list of Strings to the end of the parcel.
+ * @param val list to write
+ */
public final void writeStringVector(ArrayList<String> val) {
writeStringVector(val.toArray(new String[val.size()]));
}
+ /**
+ * Write a hwbinder object to the end of the parcel.
+ * @param binder value to write
+ */
public native final void writeStrongBinder(IHwBinder binder);
+ /**
+ * Checks to make sure that the interface name matches the name written by the parcel
+ * sender by writeInterfaceToken
+ *
+ * @throws SecurityException interface doesn't match
+ */
public native final void enforceInterface(String interfaceName);
+
+ /**
+ * Reads a boolean value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final boolean readBool();
+ /**
+ * Reads a byte value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final byte readInt8();
+ /**
+ * Reads a short value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final short readInt16();
+ /**
+ * Reads a int value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final int readInt32();
+ /**
+ * Reads a long value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final long readInt64();
+ /**
+ * Reads a float value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final float readFloat();
+ /**
+ * Reads a double value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final double readDouble();
+ /**
+ * Reads a String value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final String readString();
+ /**
+ * Reads an array of boolean values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final boolean[] readBoolVectorAsArray();
+ /**
+ * Reads an array of byte values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final byte[] readInt8VectorAsArray();
+ /**
+ * Reads an array of short values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final short[] readInt16VectorAsArray();
+ /**
+ * Reads an array of int values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final int[] readInt32VectorAsArray();
+ /**
+ * Reads an array of long values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final long[] readInt64VectorAsArray();
+ /**
+ * Reads an array of float values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final float[] readFloatVectorAsArray();
+ /**
+ * Reads an array of double values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final double[] readDoubleVectorAsArray();
+ /**
+ * Reads an array of String values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final String[] readStringVectorAsArray();
+ /**
+ * Convenience method to read a Boolean vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Boolean> readBoolVector() {
Boolean[] array = HwBlob.wrapArray(readBoolVectorAsArray());
return new ArrayList<Boolean>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Byte vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Byte> readInt8Vector() {
Byte[] array = HwBlob.wrapArray(readInt8VectorAsArray());
return new ArrayList<Byte>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Short vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Short> readInt16Vector() {
Short[] array = HwBlob.wrapArray(readInt16VectorAsArray());
return new ArrayList<Short>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Integer vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Integer> readInt32Vector() {
Integer[] array = HwBlob.wrapArray(readInt32VectorAsArray());
return new ArrayList<Integer>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Long vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Long> readInt64Vector() {
Long[] array = HwBlob.wrapArray(readInt64VectorAsArray());
return new ArrayList<Long>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Float vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Float> readFloatVector() {
Float[] array = HwBlob.wrapArray(readFloatVectorAsArray());
return new ArrayList<Float>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Double vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Double> readDoubleVector() {
Double[] array = HwBlob.wrapArray(readDoubleVectorAsArray());
return new ArrayList<Double>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a String vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<String> readStringVector() {
return new ArrayList<String>(Arrays.asList(readStringVectorAsArray()));
}
+ /**
+ * Reads a strong binder value from the parcel.
+ * @return binder object read from parcel or null if no binder can be read
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final IHwBinder readStrongBinder();
- // Handle is stored as part of the blob.
+ /**
+ * Read opaque segment of data as a blob.
+ * @return blob of size expectedSize
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final HwBlob readBuffer(long expectedSize);
+ /**
+ * Read a buffer written using scatter gather.
+ *
+ * @param expectedSize size that buffer should be
+ * @param parentHandle handle from which to read the embedded buffer
+ * @param offset offset into parent
+ * @param nullable whether or not to allow for a null return
+ * @return blob of data with size expectedSize
+ * @throws NoSuchElementException if an embedded buffer is not available to read
+ * @throws IllegalArgumentException if expectedSize < 0
+ * @throws NullPointerException if the transaction specified the blob to be null
+ * but nullable is false
+ */
public native final HwBlob readEmbeddedBuffer(
long expectedSize, long parentHandle, long offset,
boolean nullable);
+ /**
+ * Write a buffer into the transaction.
+ * @param blob blob to write into the parcel.
+ */
public native final void writeBuffer(HwBlob blob);
-
+ /**
+ * Write a status value into the blob.
+ * @param status value to write
+ */
public native final void writeStatus(int status);
+ /**
+ * @throws IllegalArgumentException if a success vaue cannot be read
+ * @throws RemoteException if success value indicates a transaction error
+ */
public native final void verifySuccess();
+ /**
+ * Should be called to reduce memory pressure when this object no longer needs
+ * to be written to.
+ */
public native final void releaseTemporaryStorage();
+ /**
+ * Should be called when object is no longer needed to reduce possible memory
+ * pressure if the Java GC does not get to this object in time.
+ */
public native final void release();
+ /**
+ * Sends the parcel to the specified destination.
+ */
public native final void send();
// Returns address of the "freeFunction".
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 619f4dc..a565dee 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -16,26 +16,70 @@
package android.os;
+import android.annotation.SystemApi;
+
/** @hide */
+@SystemApi
public interface IHwBinder {
// These MUST match their corresponding libhwbinder/IBinder.h definition !!!
+ /** @hide */
public static final int FIRST_CALL_TRANSACTION = 1;
+ /** @hide */
public static final int FLAG_ONEWAY = 1;
+ /**
+ * Process a hwbinder transaction.
+ *
+ * @param code interface specific code for interface.
+ * @param request parceled transaction
+ * @param reply object to parcel reply into
+ * @param flags transaction flags to be chosen by wire protocol
+ *
+ * @hide
+ */
+ @SystemApi
public void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Return as IHwInterface instance only if this implements descriptor.
+ *
+ * @param descriptor for example foo.bar@1.0::IBaz
+ * @hide
+ */
+ @SystemApi
public IHwInterface queryLocalInterface(String descriptor);
/**
* Interface for receiving a callback when the process hosting a service
* has gone away.
*/
+ @SystemApi
public interface DeathRecipient {
+ /**
+ * Callback for a registered process dying.
+ *
+ * @param cookie cookie this death recipient was registered with.
+ */
+ @SystemApi
public void serviceDied(long cookie);
}
+ /**
+ * Notifies the death recipient with the cookie when the process containing
+ * this binder dies.
+ *
+ * @param recipient callback object to be called on object death.
+ * @param cookie value to be given to callback on object death.
+ */
+ @SystemApi
public boolean linkToDeath(DeathRecipient recipient, long cookie);
+ /**
+ * Unregisters the death recipient from this binder.
+ *
+ * @param recipient callback to no longer recieve death notifications on this binder.
+ */
+ @SystemApi
public boolean unlinkToDeath(DeathRecipient recipient);
}
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index 7c5ac6f..1d9e2b0 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -16,7 +16,13 @@
package android.os;
+import android.annotation.SystemApi;
/** @hide */
+@SystemApi
public interface IHwInterface {
+ /**
+ * @return the binder object that corresponds to this interface.
+ */
+ @SystemApi
public IHwBinder asBinder();
}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 624e28a..0b98ecf 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -254,6 +254,7 @@
} else if (record != null) {
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
+ nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
}
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index fa96dd3..0f4fc36 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -89,6 +89,8 @@
public static final long TRACE_TAG_NETWORK = 1L << 21;
/** @hide */
public static final long TRACE_TAG_ADB = 1L << 22;
+ /** @hide */
+ public static final long TRACE_TAG_AIDL = 1L << 24;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index ecec448..5bf051d 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -4,6 +4,9 @@
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -356,6 +359,15 @@
return result;
}
+ /**
+ * Temporary dummy function.
+ * @hide
+ */
+ public ArrayList<WorkChain> getWorkChains() {
+ return null;
+ }
+
+
private boolean removeUids(WorkSource other) {
int N1 = mNum;
final int[] uids1 = mUids;
@@ -664,6 +676,52 @@
}
}
+ /**
+ * Temporary dummy class for WorkChain. Will be removed.
+ * @hide
+ */
+ public static final class WorkChain implements Parcelable {
+ // @VisibleForTesting
+ public WorkChain() {
+ }
+
+ private WorkChain(Parcel in) {
+ in.readInt();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public int[] getUids() {
+ return null;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public String[] getTags() {
+ return null;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(0);
+ }
+
+ public static final Parcelable.Creator<WorkChain> CREATOR =
+ new Parcelable.Creator<WorkChain>() {
+ public WorkChain createFromParcel(Parcel in) {
+ return new WorkChain(in);
+ }
+ public WorkChain[] newArray(int size) {
+ return new WorkChain[size];
+ }
+ };
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 57418c8..939c190 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -32,6 +32,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -158,6 +159,13 @@
private final Object mLock = new Object();
/**
+ * List of exemptions to the API blacklist. These are prefix matches on the runtime format
+ * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+ * list.
+ */
+ private List<String> mApiBlacklistExemptions = Collections.emptyList();
+
+ /**
* The state of the connection to the primary zygote.
*/
private ZygoteState primaryZygoteState;
@@ -175,7 +183,7 @@
* The process will continue running after this function returns.
*
* <p>If processes are not enabled, a new thread in the caller's
- * process is created and main() of <var>processClass</var> called there.
+ * process is created and main() of <var>processclass</var> called there.
*
* <p>The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
@@ -454,6 +462,49 @@
}
/**
+ * Push hidden API blacklisting exemptions into the zygote process(es).
+ *
+ * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+ * this call.
+ *
+ * @param exemptions List of hidden API exemption prefixes.
+ */
+ public void setApiBlacklistExemptions(List<String> exemptions) {
+ synchronized (mLock) {
+ mApiBlacklistExemptions = exemptions;
+ maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+ maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+ if (state == null || state.isClosed()) {
+ return;
+ }
+ if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
+ return;
+ }
+ try {
+ state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+ state.writer.newLine();
+ state.writer.write("--set-api-blacklist-exemptions");
+ state.writer.newLine();
+ for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
+ state.writer.write(mApiBlacklistExemptions.get(i));
+ state.writer.newLine();
+ }
+ state.writer.flush();
+ int status = state.inputStream.readInt();
+ if (status != 0) {
+ Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
+ }
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+ }
+ }
+
+ /**
* Tries to open socket to Zygote process if not already open. If
* already open, does nothing. May block and retry. Requires that mLock be held.
*/
@@ -467,8 +518,8 @@
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
+ maybeSetApiBlacklistExemptions(primaryZygoteState, false);
}
-
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
@@ -480,6 +531,7 @@
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
+ maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
}
if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 8aef012..67c6fb9 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
+import android.telecom.Log;
/**
* <p>
@@ -261,9 +262,16 @@
*/
@WorkerThread
public static boolean isBlocked(Context context, String phoneNumber) {
- final Bundle res = context.getContentResolver().call(
- AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
- return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+ try {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
+ return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "isBlocked: provider not ready.");
+ return false;
+ }
}
/**
@@ -297,9 +305,16 @@
* @return {@code true} if the current user can block numbers.
*/
public static boolean canCurrentUserBlockNumbers(Context context) {
- final Bundle res = context.getContentResolver().call(
- AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
- return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+ try {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
+ return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
+ return false;
+ }
}
/**
@@ -368,8 +383,14 @@
* the provider unless {@link #endBlockSuppression(Context)} is called.
*/
public static void notifyEmergencyContact(Context context) {
- context.getContentResolver().call(
- AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+ try {
+ context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "notifyEmergencyContact: provider not ready.");
+ }
}
/**
@@ -394,9 +415,16 @@
*/
public static boolean shouldSystemBlockNumber(Context context, String phoneNumber,
Bundle extras) {
- final Bundle res = context.getContentResolver().call(
- AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
- return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+ try {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
+ return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "shouldSystemBlockNumber: provider not ready.");
+ return false;
+ }
}
/**
@@ -416,9 +444,16 @@
* @return {@code true} if should show emergency call notification. {@code false} otherwise.
*/
public static boolean shouldShowEmergencyCallNotification(Context context) {
- final Bundle res = context.getContentResolver().call(
- AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
- return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+ try {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
+ return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
+ return false;
+ }
}
/**
@@ -436,9 +471,16 @@
public static boolean getEnhancedBlockSetting(Context context, String key) {
Bundle extras = new Bundle();
extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
- final Bundle res = context.getContentResolver().call(
- AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
- return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+ try {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
+ return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+ } catch (NullPointerException | IllegalArgumentException ex) {
+ // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+ // either of these happen.
+ Log.w(null, "getEnhancedBlockSetting: provider not ready.");
+ return false;
+ }
}
/**
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 65ce67f..5db3c1a 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -39,7 +39,7 @@
*
* @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
*/
-public class Channel {
+public final class Channel implements java.nio.channels.Channel {
private static final String TAG = "OMAPI.Channel";
private Session mSession;
@@ -47,7 +47,8 @@
private final SEService mService;
private final Object mLock = new Object();
- Channel(SEService service, Session session, ISecureElementChannel channel) {
+ Channel(@NonNull SEService service, @NonNull Session session,
+ @NonNull ISecureElementChannel channel) {
if (service == null || session == null || channel == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
@@ -63,7 +64,7 @@
* before closing the channel.
*/
public void close() {
- if (!isClosed()) {
+ if (isOpen()) {
synchronized (mLock) {
try {
mChannel.close();
@@ -75,21 +76,21 @@
}
/**
- * Tells if this channel is closed.
+ * Tells if this channel is open.
*
- * @return <code>true</code> if the channel is closed or in case of an error.
- * <code>false</code> otherwise.
+ * @return <code>false</code> if the channel is closed or in case of an error.
+ * <code>true</code> otherwise.
*/
- public boolean isClosed() {
+ public boolean isOpen() {
if (!mService.isConnected()) {
Log.e(TAG, "service not connected to system");
- return true;
+ return false;
}
try {
- return mChannel.isClosed();
+ return !mChannel.isClosed();
} catch (RemoteException e) {
Log.e(TAG, "Exception in isClosed()");
- return true;
+ return false;
}
}
@@ -158,7 +159,7 @@
* @throws SecurityException if the command is filtered by the security policy.
* @throws NullPointerException if command is NULL.
*/
- public @NonNull byte[] transmit(byte[] command) throws IOException {
+ public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
if (!mService.isConnected()) {
throw new IllegalStateException("service not connected to system");
}
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index e0c6e04..e9dd181 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -24,8 +24,4 @@
* @hide
*/
interface ISecureElementListener {
- /**
- * Called by the framework when the service is connected.
- */
- void serviceConnected();
}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 3dec976..80262f7 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -37,7 +37,7 @@
*
* @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
*/
-public class Reader {
+public final class Reader {
private static final String TAG = "OMAPI.Reader";
private final String mName;
@@ -46,7 +46,7 @@
private final Object mLock = new Object();
- Reader(SEService service, String name, ISecureElementReader reader) {
+ Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
if (reader == null || service == null || name == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d59e86a..14727f0 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -32,6 +32,7 @@
import android.util.Log;
import java.util.HashMap;
+import java.util.concurrent.Executor;
/**
* The SEService realises the communication to available Secure Elements on the
@@ -40,7 +41,7 @@
*
* @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
*/
-public class SEService {
+public final class SEService {
/**
* Error code used with ServiceSpecificException.
@@ -62,17 +63,38 @@
/**
* Interface to send call-backs to the application when the service is connected.
*/
- public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+ public interface OnConnectedListener {
+ /**
+ * Called by the framework when the service is connected.
+ */
+ void onConnected();
+ }
+
+ /**
+ * Listener object that allows the notification of the caller if this
+ * SEService could be bound to the backend.
+ */
+ private class SEListener extends ISecureElementListener.Stub {
+ public OnConnectedListener mListener = null;
+ public Executor mExecutor = null;
+
@Override
public IBinder asBinder() {
return this;
}
- /**
- * Called by the framework when the service is connected.
- */
- public void serviceConnected() {};
+ public void onConnected() {
+ if (mListener != null && mExecutor != null) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onConnected();
+ }
+ });
+ }
+ }
}
+ private SEListener mSEListener = new SEListener();
private static final String TAG = "OMAPI.SEService";
@@ -95,34 +117,32 @@
private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
/**
- * Listener object that allows the notification of the caller if this
- * SEService could be bound to the backend.
- */
- private ISecureElementListener mSEListener;
-
- /**
* Establishes a new connection that can be used to connect to all the
* Secure Elements available in the system. The connection process can be
* quite long, so it happens in an asynchronous way. It is usable only if
* the specified listener is called or if isConnected() returns
* <code>true</code>. <br>
* The call-back object passed as a parameter will have its
- * serviceConnected() method called when the connection actually happen.
+ * onConnected() method called when the connection actually happen.
*
* @param context
* the context of the calling application. Cannot be
* <code>null</code>.
* @param listener
- * a SecureElementListener object. Can be <code>null</code>.
+ * a OnConnectedListener object.
+ * @param executor
+ * an Executor which will be used when invoking the callback.
*/
- public SEService(Context context, SecureElementListener listener) {
+ public SEService(@NonNull Context context, @NonNull Executor executor,
+ @NonNull OnConnectedListener listener) {
- if (context == null) {
- throw new NullPointerException("context must not be null");
+ if (context == null || listener == null || executor == null) {
+ throw new NullPointerException("Arguments must not be null");
}
mContext = context;
- mSEListener = listener;
+ mSEListener.mListener = listener;
+ mSEListener.mExecutor = executor;
mConnection = new ServiceConnection() {
@@ -131,9 +151,7 @@
mSecureElementService = ISecureElementService.Stub.asInterface(service);
if (mSEListener != null) {
- try {
- mSEListener.serviceConnected();
- } catch (RemoteException ignore) { }
+ mSEListener.onConnected();
}
Log.i(TAG, "Service onServiceConnected");
}
@@ -164,12 +182,12 @@
}
/**
- * Returns the list of available Secure Element readers.
+ * Returns an array of available Secure Element readers.
* There must be no duplicated objects in the returned list.
* All available readers shall be listed even if no card is inserted.
*
- * @return The readers list, as an array of Readers. If there are no
- * readers the returned array is of length 0.
+ * @return An array of Readers. If there are no readers the returned array
+ * is of length 0.
*/
public @NonNull Reader[] getReaders() {
if (mSecureElementService == null) {
@@ -205,7 +223,8 @@
* (including any binding to an underlying service).
* As a result isConnected() will return false after shutdown() was called.
* After this method call, the SEService object is not connected.
- * It is recommended to call this method in the termination method of the calling application
+ * This method should be called when connection to the Secure Element is not needed
+ * or in the termination method of the calling application
* (or part of this application) which is bound to this SEService.
*/
public void shutdown() {
@@ -233,7 +252,7 @@
*
* @return String containing the OpenMobile API version (e.g. "3.0").
*/
- public String getVersion() {
+ public @NonNull String getVersion() {
return "3.2";
}
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index 3d8b74b..d5f8c82 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -39,7 +39,7 @@
*
* @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
*/
-public class Session {
+public final class Session {
private final Object mLock = new Object();
private final SEService mService;
@@ -47,7 +47,8 @@
private final ISecureElementSession mSession;
private static final String TAG = "OMAPI.Session";
- Session(SEService service, ISecureElementSession session, Reader reader) {
+ Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+ @NonNull Reader reader) {
if (service == null || reader == null || session == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
@@ -195,7 +196,8 @@
* supported by the device
* @return an instance of Channel if available or null.
*/
- public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+ public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+ throws IOException {
if (!mService.isConnected()) {
throw new IllegalStateException("service not connected to system");
}
@@ -244,7 +246,7 @@
* supported by the device
* @return an instance of Channel if available or null.
*/
- public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
+ public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
return openBasicChannel(aid, (byte) 0x00);
}
@@ -300,7 +302,8 @@
* @return an instance of Channel. Null if the Secure Element is unable to
* provide a new logical channel.
*/
- public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
+ public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+ throws IOException {
if (!mService.isConnected()) {
throw new IllegalStateException("service not connected to system");
}
@@ -352,7 +355,7 @@
* @return an instance of Channel. Null if the Secure Element is unable to
* provide a new logical channel.
*/
- public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
+ public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
return openLogicalChannel(aid, (byte) 0x00);
}
}
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index a4ffbe4..433dd3a 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -35,6 +35,11 @@
public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__PLAY = 1;
public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__STOP = 2;
+ public static final int BLE_SCAN_STATE_CHANGED = 2;
+ public static final int BLE_SCAN_STATE_CHANGED__STATE__OFF = 0;
+ public static final int BLE_SCAN_STATE_CHANGED__STATE__ON = 1;
+ public static final int BLE_SCAN_STATE_CHANGED__STATE__RESET = 2;
+
private StatsLog() {}
public static void write(int id, int field1) {}
@@ -43,4 +48,12 @@
public static void write_non_chained(int id, int uid, String tag,
int field1, int field2, String field3) {}
+
+ /** I am a dummy javadoc comment. */
+ public static void write(int code, int[] uid, String[] tag, int arg2,
+ boolean arg3, boolean arg4, boolean arg5) {};
+
+ /** I am a dummy javadoc comment. */
+ public static void write_non_chained(int code, int arg1, String arg2, int arg3,
+ boolean arg4, boolean arg5, boolean arg6) {};
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 8172a20..8561bf0 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -54,11 +54,6 @@
private static final boolean USE_NATIVE_PARSING = true;
private static final boolean SANITY_CHECK_NATIVE = false;
- private static final String CLATD_INTERFACE_PREFIX = "v4-";
- // Delta between IPv4 header (20b) and IPv6 header (40b).
- // Used for correct stats accounting on clatd interfaces.
- private static final int IPV4V6_HEADER_DELTA = 20;
-
/** Path to {@code /proc/net/dev}. */
private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
@@ -94,7 +89,7 @@
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included.
*/
- public static String[] augmentWithStackedInterfacesLocked(@Nullable String[] requiredIfaces) {
+ public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null;
}
@@ -116,6 +111,15 @@
return relatedIfaces.toArray(outArray);
}
+ /**
+ * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
+ * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
+ */
+ public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+ NetworkStats stackedTraffic) {
+ NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
+ }
+
@VisibleForTesting
public static void clearStackedIfaces() {
sStackedIfaces.clear();
@@ -287,48 +291,10 @@
NetworkStats lastStats) throws IOException {
final NetworkStats stats =
readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
- // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
- // sStackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
- final NetworkStats adjustments = new NetworkStats(0, sStackedIfaces.size());
- NetworkStats.Entry entry = null; // For recycling
-
- // For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
- // packet on the stacked interface, and once as translated to an IPv6 packet on the
- // base interface. For correct stats accounting on the base interface, every 464xlat
- // packet needs to be subtracted from the root UID on the base interface both for tx
- // and rx traffic (http://b/12249687, http:/b/33681750).
- for (int i = 0; i < stats.size(); i++) {
- entry = stats.getValues(i, entry);
- if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
- continue;
- }
- final String baseIface = sStackedIfaces.get(entry.iface);
- if (baseIface == null) {
- continue;
- }
-
- NetworkStats.Entry adjust =
- new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
- // Subtract any 464lat traffic seen for the root UID on the current base interface.
- adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
- adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
- adjust.rxPackets -= entry.rxPackets;
- adjust.txPackets -= entry.txPackets;
- adjustments.combineValues(adjust);
-
- // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
- // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
- // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
- // difference for all packets (http://b/12249687, http:/b/33681750).
- entry.rxBytes = entry.rxPackets * IPV4V6_HEADER_DELTA;
- entry.txBytes = entry.txPackets * IPV4V6_HEADER_DELTA;
- entry.rxPackets = 0;
- entry.txPackets = 0;
- stats.combineValues(entry);
- }
-
- stats.combineAllValues(adjustments);
+ // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
+ // TODO: remove this and only apply adjustments in NetworkStatsService.
+ stats.apply464xlatAdjustments(sStackedIfaces);
return stats;
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index bb5a0ad..a9cd5c8 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -34,6 +34,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.Objects;
import java.util.TimeZone;
import java.util.logging.LogManager;
import org.apache.harmony.luni.internal.util.TimezoneGetter;
@@ -67,8 +68,12 @@
* but apps can override that behavior.
*/
private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
+ public volatile boolean mTriggered = false;
+
@Override
public void uncaughtException(Thread t, Throwable e) {
+ mTriggered = true;
+
// Don't re-enter if KillApplicationHandler has already run
if (mCrashing) return;
@@ -96,12 +101,33 @@
/**
* Handle application death from an uncaught exception. The framework
* catches these for the main threads, so this should only matter for
- * threads created by applications. Before this method runs,
- * {@link LoggingHandler} will already have logged details.
+ * threads created by applications. Before this method runs, the given
+ * instance of {@link LoggingHandler} should already have logged details
+ * (and if not it is run first).
*/
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
+ private final LoggingHandler mLoggingHandler;
+
+ /**
+ * Create a new KillApplicationHandler that follows the given LoggingHandler.
+ * If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
+ * on the created instance without {@code loggingHandler} having been triggered,
+ * {@link LoggingHandler#uncaughtException(Thread, Throwable)
+ * loggingHandler.uncaughtException} will be called first.
+ *
+ * @param loggingHandler the {@link LoggingHandler} expected to have run before
+ * this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
+ * is being called.
+ */
+ public KillApplicationHandler(LoggingHandler loggingHandler) {
+ this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
+ }
+
+ @Override
public void uncaughtException(Thread t, Throwable e) {
try {
+ ensureLogging(t, e);
+
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
@@ -132,6 +158,33 @@
System.exit(10);
}
}
+
+ /**
+ * Ensures that the logging handler has been triggered.
+ *
+ * See b/73380984. This reinstates the pre-O behavior of
+ *
+ * {@code thread.getUncaughtExceptionHandler().uncaughtException(thread, e);}
+ *
+ * logging the exception (in addition to killing the app). This behavior
+ * was never documented / guaranteed but helps in diagnostics of apps
+ * using the pattern.
+ *
+ * If this KillApplicationHandler is invoked the "regular" way (by
+ * {@link Thread#dispatchUncaughtException(Throwable)
+ * Thread.dispatchUncaughtException} in case of an uncaught exception)
+ * then the pre-handler (expected to be {@link #mLoggingHandler}) will already
+ * have run. Otherwise, we manually invoke it here.
+ */
+ private void ensureLogging(Thread t, Throwable e) {
+ if (!mLoggingHandler.mTriggered) {
+ try {
+ mLoggingHandler.uncaughtException(t, e);
+ } catch (Throwable loggingThrowable) {
+ // Ignored.
+ }
+ }
+ }
}
protected static final void commonInit() {
@@ -141,8 +194,9 @@
* set handlers; these apply to all threads in the VM. Apps can replace
* the default handler, but not the pre handler.
*/
- Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
- Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
+ LoggingHandler loggingHandler = new LoggingHandler();
+ Thread.setUncaughtExceptionPreHandler(loggingHandler);
+ Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
/*
* Install a TimezoneGetter subclass for ZoneInfo.db
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a32fb43..adc5508 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -47,6 +47,8 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+
import libcore.io.IoUtils;
/**
@@ -159,6 +161,11 @@
return null;
}
+ if (parsedArgs.apiBlacklistExemptions != null) {
+ handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+ return null;
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -278,6 +285,15 @@
}
}
+ private void handleApiBlacklistExemptions(String[] exemptions) {
+ try {
+ ZygoteInit.setApiBlacklistExemptions(exemptions);
+ mSocketOutStream.writeInt(0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+ }
+
protected void preload() {
ZygoteInit.lazyPreload();
}
@@ -424,6 +440,12 @@
boolean startChildZygote;
/**
+ * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
+ * or when they change, via --set-api-blacklist-exemptions.
+ */
+ String[] apiBlacklistExemptions;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -576,6 +598,11 @@
preloadDefault = true;
} else if (arg.equals("--start-child-zygote")) {
startChildZygote = true;
+ } else if (arg.equals("--set-api-blacklist-exemptions")) {
+ // consume all remaining args; this is a stand-alone command, never included
+ // with the regular fork command.
+ apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+ curArg = args.length;
} else {
break;
}
@@ -590,7 +617,7 @@
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
- } else if (!preloadDefault) {
+ } else if (!preloadDefault && apiBlacklistExemptions == null) {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a05454f..c8e7102 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -514,6 +514,10 @@
/* should never reach here */
}
+ public static void setApiBlacklistExemptions(String[] exemptions) {
+ VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
+ }
+
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries. The
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6c46cfc..7f6d19b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -591,7 +591,6 @@
{
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
- char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
@@ -673,15 +672,7 @@
executionMode = kEMJitCompiler;
}
- // If dalvik.vm.stack-trace-dir is set, it enables the "new" stack trace
- // dump scheme and a new file is created for each stack dump. If it isn't set,
- // the old scheme is enabled.
- property_get("dalvik.vm.stack-trace-dir", propBuf, "");
- if (strlen(propBuf) > 0) {
- addOption("-Xusetombstonedtraces");
- } else {
- parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");
- }
+ addOption("-Xusetombstonedtraces");
strcpy(jniOptsBuf, "-Xjniopts:");
if (parseRuntimeOption("dalvik.vm.jniopts", jniOptsBuf, "-Xjniopts:")) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index f1b736b..e5281ff 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -260,7 +260,7 @@
}
// Apply system or app filter based on uid.
- if (getuid() >= AID_APP_START) {
+ if (uid >= AID_APP_START) {
set_app_seccomp_filter();
} else {
set_system_seccomp_filter();
@@ -619,11 +619,6 @@
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
}
- // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to
- // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
- // b/71859146).
- SetUpSeccompFilter(uid);
-
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
if (!EnableKeepCapabilities(&error_msg)) {
@@ -697,6 +692,13 @@
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
}
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+ // uid from 0, which clears capabilities. The other alternative is to call
+ // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146). As the result, privileged syscalls used below still need to be accessible in
+ // app process.
+ SetUpSeccompFilter(uid);
+
rc = setresuid(uid, uid, uid);
if (rc == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b13d0b..20a5afe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -178,6 +178,8 @@
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f5b350b..8947bf0 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" free="3214|1017" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="3214|1017" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -80,7 +80,7 @@
<shortcode country="cn" premium="1066.*" free="1065.*" />
<!-- Colombia: 1-6 digits (not confirmed) -->
- <shortcode country="co" pattern="\\d{1,6}" free="890350" />
+ <shortcode country="co" pattern="\\d{1,6}" free="890350|908160" />
<!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
@@ -126,7 +126,7 @@
<shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
<!-- Croatia -->
- <shortcode country="hr" free="13062" />
+ <shortcode country="hr" pattern="\\d{1,5}" free="13062" />
<!-- Hungary: 4 or 10 digits starting with 1 or 0, plus EU:
http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
@@ -150,7 +150,7 @@
<shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503" standard="43\\d{3}" />
<!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
- <shortcode country="jp" free="8083" />
+ <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
<!-- Kenya: 5 digits, known premium codes listed -->
<shortcode country="ke" pattern="\\d{5}" free="21725" />
@@ -165,10 +165,10 @@
<shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
<!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006" />
+ <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991" />
<!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
- <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399" />
+ <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
<!-- Luxembourg: 5 digits, 6xxxx, plus EU:
http://www.luxgsm.lu/assets/files/filepage/file_1253803400.pdf -->
@@ -199,10 +199,10 @@
<shortcode country="pe" pattern="\\d{4,5}" free="9963" />
<!-- Philippines -->
- <shortcode country="ph" free="2147|5495|5496" />
+ <shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
<!-- Pakistan -->
- <shortcode country="pk" free="2057" />
+ <shortcode country="pk" pattern="\\d{1,5}" free="2057" />
<!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
@@ -224,7 +224,7 @@
<shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" free="6954|8501" standard="2037|2044"/>
<!-- Saudi Arabia -->
- <shortcode country="sa" free="8145" />
+ <shortcode country="sa" pattern="\\d{1,5}" free="8145" />
<!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
<shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
@@ -240,13 +240,13 @@
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
<!-- Thailand: 4186001 used by AIS_TH_DCB -->
- <shortcode country="th" free="4186001" />
+ <shortcode country="th" pattern="\\d{1,5}" free="4186001" />
<!-- Tajikistan: 4 digits, known premium codes listed -->
<shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
<!-- Turkey -->
- <shortcode country="tr" free="7529|5528|6493" />
+ <shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493" />
<!-- Ukraine: 4 digits, known premium codes listed -->
<shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index b767046..11360a1 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -62,7 +62,7 @@
"uniform mediump vec4 roundRectInnerRectLTWH;\n"
"uniform mediump float roundRectRadius;\n";
const char* gVS_Header_Varyings_HasTexture =
- "varying vec2 outTexCoords;\n";
+ "varying highp vec2 outTexCoords;\n";
const char* gVS_Header_Varyings_HasColors =
"varying vec4 outColors;\n";
const char* gVS_Header_Varyings_HasVertexAlpha =
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index 9043672..0042ff9 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -34,10 +34,6 @@
# This only works if the class name matches the file name and the directory structure
# matches the package.
local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
-# Utility variables to allow replacing a space with a comma
-comma:= ,
-empty:=
-space:= $(empty) $(empty)
# Convert class name list to jacoco exclude list
# This appends a * to all classes and replace the space separators with commas.
# These patterns will match all classes in this module and their inner classes.
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3d5ee39..65352ed 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -152,7 +152,6 @@
AHardwareBuffer_unlock; # introduced=26
ANativeWindow_acquire;
ANativeWindow_fromSurface;
- ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13
ANativeWindow_toSurface; # introduced=26
ANativeWindow_getFormat;
ANativeWindow_getHeight;
diff --git a/packages/CaptivePortalLogin/Android.mk b/packages/CaptivePortalLogin/Android.mk
index 7dc23ff..8a96b16 100644
--- a/packages/CaptivePortalLogin/Android.mk
+++ b/packages/CaptivePortalLogin/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 services.net
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 4db0034..2a1bbed 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -30,6 +30,7 @@
import android.net.NetworkRequest;
import android.net.Proxy;
import android.net.Uri;
+import android.net.dns.ResolvUtil;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
@@ -42,6 +43,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.webkit.CookieManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
@@ -117,6 +119,8 @@
// Also initializes proxy system properties.
mCm.bindProcessToNetwork(mNetwork);
+ mCm.setProcessDefaultNetworkForHostResolution(
+ ResolvUtil.getNetworkWithUseLocalNameserversFlag(mNetwork));
// Proxy system properties must be initialized before setContentView is called because
// setContentView initializes the WebView logic which in turn reads the system properties.
@@ -147,6 +151,7 @@
final WebView webview = getWebview();
webview.clearCache(true);
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
index df88afd..5068b3b 100644
--- a/packages/CarrierDefaultApp/Android.mk
+++ b/packages/CarrierDefaultApp/Android.mk
@@ -9,6 +9,8 @@
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
+LOCAL_STATIC_JAVA_LIBRARIES := services.net
+
include $(BUILD_PACKAGE)
# This finds and builds the test apk as well, so a single make does both.
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 95ec83d..7479d9a 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -32,6 +32,7 @@
import android.net.Proxy;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.dns.ResolvUtil;
import android.net.http.SslError;
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
@@ -115,6 +116,8 @@
requestNetworkForCaptivePortal();
} else {
mCm.bindProcessToNetwork(mNetwork);
+ mCm.setProcessDefaultNetworkForHostResolution(
+ ResolvUtil.getNetworkWithUseLocalNameserversFlag(mNetwork));
// Start initial page load so WebView finishes loading proxy settings.
// Actual load of mUrl is initiated by MyWebViewClient.
mWebView.loadData("", "text/html", null);
diff --git a/packages/InputDevices/res/raw/keyboard_layout_polish.kcm b/packages/InputDevices/res/raw/keyboard_layout_polish.kcm
new file mode 100644
index 0000000..559ec07
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_polish.kcm
@@ -0,0 +1,327 @@
+# Copyright (C) 2018 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.
+
+#
+# Polish (qwerty) keyboard layout.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ralt: '\u0119'
+ ralt+shift, ralt+capslock: '\u0118'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ralt: '\u00F3'
+ ralt+shift, ralt+capslock: '\u00D3'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base: '['
+ shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base: ']'
+ shift: '}'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+ ralt: '\u0105'
+ ralt+shift, ralt+capslock: '\u0104'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ ralt: '\u015b'
+ ralt+shift, ralt+capslock: '\u015a'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+ ralt: '\u0142'
+ ralt+shift, ralt+capslock: '\u0141'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+### ROW 4
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+ ralt: '\u017c'
+ ralt+shift, ralt+capslock: '\u017b'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+ ralt: '\u017a'
+ ralt+shift, ralt+capslock: '\u0179'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ ralt: '\u0107'
+ ralt+shift, ralt+capslock: '\u0106'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ ralt: '\u0144'
+ ralt+shift, ralt+capslock: '\u0143'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 61d3234..5fdc4a6 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -125,4 +125,7 @@
<!-- Azerbaijani keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_azerbaijani">Azerbaijani</string>
+
+ <!-- Polish keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_polish">Polish</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index c6bfc1f..1807aea 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -159,4 +159,8 @@
<keyboard-layout android:name="keyboard_layout_azerbaijani"
android:label="@string/keyboard_layout_azerbaijani"
android:keyboardLayout="@raw/keyboard_layout_azerbaijani" />
+
+ <keyboard-layout android:name="keyboard_layout_polish"
+ android:label="@string/keyboard_layout_polish"
+ android:keyboardLayout="@raw/keyboard_layout_polish" />
</keyboard-layouts>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 428f0b8..61b88a5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -456,10 +456,10 @@
<string name="keep_screen_on">Stay awake</string>
<!-- setting Checkbox summary whether to keep the screen on when plugged in -->
<string name="keep_screen_on_summary">Screen will never sleep while charging</string>
- <!-- Setting Checkbox title whether to enable bluetooth HCI snoop log -->
+ <!-- Setting Checkbox title whether to enable Bluetooth HCI snoop log -->
<string name="bt_hci_snoop_log">Enable Bluetooth HCI snoop log</string>
- <!-- setting Checkbox summary whether to capture all bluetooth HCI packets in a file -->
- <string name="bt_hci_snoop_log_summary">Capture all bluetooth HCI packets in a file</string>
+ <!-- setting Checkbox summary whether to capture all Bluetooth HCI packets in a file -->
+ <string name="bt_hci_snoop_log_summary">Capture all Bluetooth HCI packets in a file (Toggle Bluetooth after changing this setting)</string>
<!-- setting Checkbox title whether to enable OEM unlock [CHAR_LIMIT=35] -->
<string name="oem_unlock_enable">OEM unlocking</string>
<!-- setting Checkbox summary whether to enable OEM unlock [CHAR_LIMIT=50] -->
@@ -501,30 +501,30 @@
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string>
- <!-- UI debug setting: Select Bluetooth Audio Codec -->
+ <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
<string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
- <!-- UI debug setting: Select Bluetooth Audio Codec -->
- <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
+ <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
+ <string name="bluetooth_select_a2dp_codec_type_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection</string>
- <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
+ <!-- UI debug setting: Trigger Bluetooth Audio Sample Rate Selection -->
<string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
- <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
+ <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate -->
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string>
- <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
+ <!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection -->
<string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
- <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
- <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
+ <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Bits Per Sample -->
+ <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Bits Per Sample</string>
- <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
+ <!-- UI debug setting: Trigger Bluetooth Audio Channel Mode Selection -->
<string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
- <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
- <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
+ <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Channel Mode -->
+ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Channel Mode</string>
- <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
+ <!-- UI debug setting: Trigger Bluetooth Audio LDAC Playback Quality Selection -->
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
<!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
- <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Trigger Bluetooth Audio LDAC Codec\u000ASelection: Playback Quality</string>
<!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
<string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 784c714..b74b2cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -115,6 +116,8 @@
new ActiveDeviceChangedHandler());
addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
new ActiveDeviceChangedHandler());
+ addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
@@ -434,6 +437,8 @@
bluetoothProfile = BluetoothProfile.A2DP;
} else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
bluetoothProfile = BluetoothProfile.HEADSET;
+ } else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.HEARING_AID;
} else {
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f6ec6a8..dc2ecea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -109,6 +109,7 @@
// Active device state
private boolean mIsActiveDeviceA2dp = false;
private boolean mIsActiveDeviceHeadset = false;
+ private boolean mIsActiveDeviceHearingAid = false;
/**
* Describes the current device and profile for logging.
@@ -416,6 +417,36 @@
}
}
+ /**
+ * Set this device as active device
+ * @return true if at least one profile on this device is set to active, false otherwise
+ */
+ public boolean setActive() {
+ boolean result = false;
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null && isConnectedProfile(a2dpProfile)) {
+ if (a2dpProfile.setActiveDevice(getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + this);
+ result = true;
+ }
+ }
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if ((headsetProfile != null) && isConnectedProfile(headsetProfile)) {
+ if (headsetProfile.setActiveDevice(getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + this);
+ result = true;
+ }
+ }
+ HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+ if ((hearingAidProfile != null) && isConnectedProfile(hearingAidProfile)) {
+ if (hearingAidProfile.setActiveDevice(getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: Hearing Aid active device=" + this);
+ result = true;
+ }
+ }
+ return result;
+ }
+
void refreshName() {
fetchName();
dispatchAttributesChanged();
@@ -478,6 +509,10 @@
changed = (mIsActiveDeviceHeadset != isActive);
mIsActiveDeviceHeadset = isActive;
break;
+ case BluetoothProfile.HEARING_AID:
+ changed = (mIsActiveDeviceHearingAid != isActive);
+ mIsActiveDeviceHearingAid = isActive;
+ break;
default:
Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
" isActive " + isActive);
@@ -501,6 +536,8 @@
return mIsActiveDeviceA2dp;
case BluetoothProfile.HEADSET:
return mIsActiveDeviceHeadset;
+ case BluetoothProfile.HEARING_AID:
+ return mIsActiveDeviceHearingAid;
default:
Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
break;
@@ -592,6 +629,10 @@
if (headsetProfile != null) {
mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
}
+ HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+ if (hearingAidProfile != null) {
+ mIsActiveDeviceHearingAid = hearingAidProfile.getActiveDevices().contains(mDevice);
+ }
}
/**
@@ -922,6 +963,7 @@
boolean profileConnected = false; // at least one profile is connected
boolean a2dpNotConnected = false; // A2DP is preferred but not connected
boolean hfpNotConnected = false; // HFP is preferred but not connected
+ boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
for (LocalBluetoothProfile profile : getProfiles()) {
int connectionStatus = getProfileConnectionState(profile);
@@ -943,6 +985,8 @@
} else if ((profile instanceof HeadsetProfile) ||
(profile instanceof HfpClientProfile)) {
hfpNotConnected = true;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidNotConnected = true;
}
}
break;
@@ -975,6 +1019,10 @@
activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
}
}
+ if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
+ activeDeviceString = activeDeviceStringsArray[1];
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+ }
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 8f9e4635..6c5ecbf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -134,6 +134,16 @@
return mService.getConnectionState(device);
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public List<BluetoothDevice> getActiveDevices() {
+ if (mService == null) return new ArrayList<>();
+ return mService.getActiveDevices();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index d6b2006..2f5eead 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -71,6 +71,8 @@
@Mock
private PanProfile mPanProfile;
@Mock
+ private HearingAidProfile mHearingAidProfile;
+ @Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@@ -100,6 +102,7 @@
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
+ when(mHearingAidProfile.isProfileReady()).thenReturn(true);
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
}
@@ -280,4 +283,63 @@
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
}
+
+ /**
+ * Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
+ */
+ @Test
+ public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ // Connect device1 for A2DP and HFP and device2 for Hearing Aid
+ cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+
+ // Verify that both devices are connected and none is Active
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+ // The first device is active for A2DP and HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+ // The second device is active for Hearing Aid and the first device is not active
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEARING_AID);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+
+ // No active device for Hearing Aid
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 2c91d5a..6593cbc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -61,6 +61,8 @@
@Mock
private PanProfile mPanProfile;
@Mock
+ private HearingAidProfile mHearingAidProfile;
+ @Mock
private BluetoothDevice mDevice;
private CachedBluetoothDevice mCachedDevice;
private Context mContext;
@@ -75,6 +77,7 @@
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
+ when(mHearingAidProfile.isProfileReady()).thenReturn(true);
mCachedDevice = spy(
new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -209,6 +212,23 @@
}
@Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+ // Test without battery level
+ // Set Hearing Aid profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for Hearing Aid and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set Hearing Aid profile to be disconnected and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+ mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
// Test without battery level
// Set A2DP and HFP profiles to be connected and test connection state summary
@@ -299,4 +319,24 @@
// Verify new alias is returned on getName
assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW);
}
+
+ @Test
+ public void testSetActive() {
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
+ when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+ when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+
+ assertThat(mCachedDevice.setActive()).isFalse();
+
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.setActive()).isTrue();
+
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.setActive()).isTrue();
+
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.setActive()).isFalse();
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ae7ac8f..76e3131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -52,6 +52,8 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
@@ -137,6 +139,7 @@
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
+import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
@@ -151,6 +154,7 @@
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -251,6 +255,7 @@
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
+ private IIpConnectivityMetrics mIpConnectivityMetrics;
private String mCurrentTcpBufferSizes;
@@ -409,6 +414,9 @@
// Handle changes in Private DNS settings.
private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
+ // Handle private DNS validation status updates.
+ private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -878,7 +886,12 @@
private Tethering makeTethering() {
// TODO: Move other elements into @Overridden getters.
- final TetheringDependencies deps = new TetheringDependencies();
+ final TetheringDependencies deps = new TetheringDependencies() {
+ @Override
+ public boolean isTetheringSupported() {
+ return ConnectivityService.this.isTetheringSupported();
+ }
+ };
return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
@@ -897,7 +910,7 @@
// Used only for testing.
// TODO: Delete this and either:
- // 1. Give Fake SettingsProvider the ability to send settings change notifications (requires
+ // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
// changing ContentResolver to make registerContentObserver non-final).
// 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
// by subclassing SettingsObserver.
@@ -906,6 +919,12 @@
mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
}
+ // See FakeSettingsProvider comment above.
+ @VisibleForTesting
+ void updatePrivateDnsSettings() {
+ mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+ }
+
private void handleMobileDataAlwaysOn() {
final boolean enable = toBool(Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
@@ -935,8 +954,8 @@
}
private void registerPrivateDnsSettingsCallbacks() {
- for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
- mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+ for (Uri uri : DnsManager.getPrivateDnsSettingsUris()) {
+ mSettingsObserver.observe(uri, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
}
@@ -989,8 +1008,12 @@
if (network == null) {
return null;
}
+ return getNetworkAgentInfoForNetId(network.netId);
+ }
+
+ private NetworkAgentInfo getNetworkAgentInfoForNetId(int netId) {
synchronized (mNetworkForNetId) {
- return mNetworkForNetId.get(network.netId);
+ return mNetworkForNetId.get(netId);
}
}
@@ -1130,9 +1153,7 @@
}
NetworkAgentInfo nai;
if (vpnNetId != NETID_UNSET) {
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(vpnNetId);
- }
+ nai = getNetworkAgentInfoForNetId(vpnNetId);
if (nai != null) return nai.network;
}
nai = getDefaultNetwork();
@@ -1507,6 +1528,41 @@
return true;
}
+ @VisibleForTesting
+ protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
+ @Override
+ public void onPrivateDnsValidationEvent(int netId, String ipAddress,
+ String hostname, boolean validated) {
+ try {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
+ new PrivateDnsValidationUpdate(netId,
+ InetAddress.parseNumericAddress(ipAddress),
+ hostname, validated)));
+ } catch (IllegalArgumentException e) {
+ loge("Error parsing ip address in validation event");
+ }
+ }
+ };
+
+ @VisibleForTesting
+ protected void registerNetdEventCallback() {
+ mIpConnectivityMetrics =
+ (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ if (mIpConnectivityMetrics == null) {
+ Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+ }
+
+ try {
+ mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
+ mNetdEventCallback);
+ } catch (Exception e) {
+ loge("Error registering netd callback: " + e);
+ }
+ }
+
private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
@@ -1691,6 +1747,7 @@
void systemReady() {
loadGlobalProxy();
+ registerNetdEventCallback();
synchronized (this) {
mSystemReady = true;
@@ -2108,41 +2165,21 @@
default:
return false;
case NetworkMonitor.EVENT_NETWORK_TESTED: {
- final NetworkAgentInfo nai;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(msg.arg2);
- }
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
- final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
- ? (PrivateDnsConfig) msg.obj : null;
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
- final boolean reevaluationRequired;
- final String logMsg;
- if (valid) {
- reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
- logMsg = (DBG && (privateDnsCfg != null))
- ? " with " + privateDnsCfg.toString() : "";
- } else {
- reevaluationRequired = false;
- logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
- ? " with redirect to " + redirectUrl : "";
- }
if (DBG) {
+ final String logMsg = !TextUtils.isEmpty(redirectUrl)
+ ? " with redirect to " + redirectUrl
+ : "";
log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
- // If there is a change in Private DNS configuration,
- // trigger reevaluation of the network to test it.
- if (reevaluationRequired) {
- nai.networkMonitor.sendMessage(
- NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
- break;
- }
if (valid != nai.lastValidated) {
if (wasDefault) {
metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
@@ -2171,10 +2208,7 @@
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
final int netId = msg.arg2;
final boolean visible = toBool(msg.arg1);
- final NetworkAgentInfo nai;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(netId);
- }
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
// If captive portal status has changed, update capabilities or disconnect.
if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
final int oldScore = nai.getCurrentScore();
@@ -2205,18 +2239,10 @@
break;
}
case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
- final NetworkAgentInfo nai;
- synchronized (mNetworkForNetId) {
- nai = mNetworkForNetId.get(msg.arg2);
- }
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
- final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
- final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
- if (nai.lastValidated && reevaluationRequired) {
- nai.networkMonitor.sendMessage(
- NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
- }
+ updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
break;
}
}
@@ -2254,61 +2280,50 @@
}
}
+ private boolean networkRequiresValidation(NetworkAgentInfo nai) {
+ return NetworkMonitor.isValidationRequired(
+ mDefaultRequest.networkCapabilities, nai.networkCapabilities);
+ }
+
private void handlePrivateDnsSettingsChanged() {
final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- // Private DNS only ever applies to networks that might provide
- // Internet access and therefore also require validation.
- if (!NetworkMonitor.isValidationRequired(
- mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
- continue;
- }
-
- // Notify the NetworkMonitor thread in case it needs to cancel or
- // schedule DNS resolutions. If a DNS resolution is required the
- // result will be sent back to us.
- nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
-
- if (!cfg.inStrictMode()) {
- // No strict mode hostname DNS resolution needed, so just update
- // DNS settings directly. In opportunistic and "off" modes this
- // just reprograms netd with the network-supplied DNS servers
- // (and of course the boolean of whether or not to attempt TLS).
- //
- // TODO: Consider code flow parity with strict mode, i.e. having
- // NetworkMonitor relay the PrivateDnsConfig back to us and then
- // performing this call at that time.
- updatePrivateDns(nai, cfg);
+ handlePerNetworkPrivateDnsConfig(nai, cfg);
+ if (networkRequiresValidation(nai)) {
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
}
}
- private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
- final boolean reevaluationRequired = true;
- final boolean dontReevaluate = false;
+ private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
+ // Private DNS only ever applies to networks that might provide
+ // Internet access and therefore also require validation.
+ if (!networkRequiresValidation(nai)) return;
- final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
+ // Notify the NetworkMonitor thread in case it needs to cancel or
+ // schedule DNS resolutions. If a DNS resolution is required the
+ // result will be sent back to us.
+ nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+
+ // With Private DNS bypass support, we can proceed to update the
+ // Private DNS config immediately, even if we're in strict mode
+ // and have not yet resolved the provider name into a set of IPs.
+ updatePrivateDns(nai, cfg);
+ }
+
+ private void updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
+ mDnsManager.updatePrivateDns(nai.network, newCfg);
updateDnses(nai.linkProperties, null, nai.network.netId);
+ }
- if (newCfg == null) {
- if (oldCfg == null) return dontReevaluate;
- return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
+ private void handlePrivateDnsValidationUpdate(PrivateDnsValidationUpdate update) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetId(update.netId);
+ if (nai == null) {
+ return;
}
-
- if (oldCfg == null) {
- return newCfg.useTls ? reevaluationRequired : dontReevaluate;
- }
-
- if (oldCfg.useTls != newCfg.useTls) {
- return reevaluationRequired;
- }
-
- if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
- return reevaluationRequired;
- }
-
- return dontReevaluate;
+ mDnsManager.updatePrivateDnsValidation(update);
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
private void updateLingerState(NetworkAgentInfo nai, long now) {
@@ -2995,6 +3010,10 @@
case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
handlePrivateDnsSettingsChanged();
break;
+ case EVENT_PRIVATE_DNS_VALIDATION_UPDATE:
+ handlePrivateDnsValidationUpdate(
+ (PrivateDnsValidationUpdate) msg.obj);
+ break;
}
}
}
@@ -3247,7 +3266,7 @@
if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
return;
}
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
+ nai.networkMonitor.forceReevaluation(uid);
}
private ProxyInfo getDefaultProxy() {
@@ -4568,6 +4587,11 @@
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
+ // Make sure LinkProperties represents the latest private DNS status.
+ // This does not need to be done before updateDnses because the
+ // LinkProperties are not the source of the private DNS configuration.
+ // updateDnses will fetch the private DNS configuration from DnsManager.
+ mDnsManager.updatePrivateDnsStatus(netId, newLp);
// Start or stop clat accordingly to network state.
networkAgent.updateClat(mNetd);
@@ -4866,7 +4890,7 @@
}
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
- if (mNetworkForNetId.get(nai.network.netId) != nai) {
+ if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
// Ignore updates for disconnected networks
return;
}
@@ -5446,6 +5470,7 @@
Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
}
+ handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
updateLinkProperties(networkAgent, null);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d09a161..bde6bd8 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -931,7 +931,7 @@
return mPort;
}
- public FileDescriptor getSocket() {
+ public FileDescriptor getFileDescriptor() {
return mSocket;
}
@@ -1065,7 +1065,10 @@
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
checkInetAddress(destinationAddress);
- /* requestedSpi can be anything in the int range, so no check is needed. */
+ // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
+ if (requestedSpi > 0 && requestedSpi < 256) {
+ throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
+ }
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 53285e6..155febd 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -587,13 +587,8 @@
}
private File dumpKernelStackTraces() {
- String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
- if (tracesPath == null || tracesPath.length() == 0) {
- return null;
- }
-
- native_dumpKernelStacks(tracesPath);
- return new File(tracesPath);
+ native_dumpKernelStacks("/data/anr");
+ return new File("/data/anr");
}
private native void native_dumpKernelStacks(String tracesPath);
@@ -615,14 +610,6 @@
return null;
}
- // Don't run the FD monitor on builds that have a global ANR trace file. We're using
- // the ANR trace directory as a quick hack in order to get these traces in bugreports
- // and we wouldn't want to overwrite something important.
- final String dumpDirStr = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
- if (dumpDirStr.isEmpty()) {
- return null;
- }
-
final StructRlimit rlimit;
try {
rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
@@ -639,7 +626,7 @@
// We do this to avoid having to enumerate the contents of /proc/self/fd in order to
// count the number of descriptors open in the process.
final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
- return new OpenFdMonitor(new File(dumpDirStr), fdThreshold);
+ return new OpenFdMonitor(new File("/data/anr"), fdThreshold);
}
OpenFdMonitor(File dumpDir, File fdThreshold) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f24d9f..7cfc8af 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2696,13 +2696,15 @@
}
/**
- * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the
+ * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the
* latest value via a content observer.
*/
static class HiddenApiBlacklist extends ContentObserver {
private final Context mContext;
private boolean mBlacklistDisabled;
+ private String mExemptionsStr;
+ private List<String> mExemptions = Collections.emptyList();
public HiddenApiBlacklist(Handler handler, Context context) {
super(handler);
@@ -2718,8 +2720,22 @@
}
private void update() {
- mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+ String exemptions = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
+ if (!TextUtils.equals(exemptions, mExemptionsStr)) {
+ mExemptionsStr = exemptions;
+ if ("*".equals(exemptions)) {
+ mBlacklistDisabled = true;
+ mExemptions = Collections.emptyList();
+ } else {
+ mBlacklistDisabled = false;
+ mExemptions = TextUtils.isEmpty(exemptions)
+ ? Collections.emptyList()
+ : Arrays.asList(exemptions.split(","));
+ }
+ zygoteProcess.setApiBlacklistExemptions(mExemptions);
+ }
+
}
boolean isDisabled() {
@@ -5594,57 +5610,20 @@
}
}
- boolean useTombstonedForJavaTraces = false;
- File tracesFile;
+ final File tracesDir = new File("/data/anr");
+ // Each set of ANR traces is written to a separate file and dumpstate will process
+ // all such files and add them to a captured bug report if they're recent enough.
+ maybePruneOldTraces(tracesDir);
- final String tracesDirProp = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
- if (tracesDirProp.isEmpty()) {
- // When dalvik.vm.stack-trace-dir is not set, we are using the "old" trace
- // dumping scheme. All traces are written to a global trace file (usually
- // "/data/anr/traces.txt") so the code below must take care to unlink and recreate
- // the file if requested.
- //
- // This mode of operation will be removed in the near future.
-
-
- String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
- if (globalTracesPath.isEmpty()) {
- Slog.w(TAG, "dumpStackTraces: no trace path configured");
- return null;
- }
-
- tracesFile = new File(globalTracesPath);
- try {
- if (clearTraces && tracesFile.exists()) {
- tracesFile.delete();
- }
-
- tracesFile.createNewFile();
- FileUtils.setPermissions(globalTracesPath, 0666, -1, -1); // -rw-rw-rw-
- } catch (IOException e) {
- Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
- return null;
- }
- } else {
- File tracesDir = new File(tracesDirProp);
- // When dalvik.vm.stack-trace-dir is set, we use the "new" trace dumping scheme.
- // Each set of ANR traces is written to a separate file and dumpstate will process
- // all such files and add them to a captured bug report if they're recent enough.
- maybePruneOldTraces(tracesDir);
-
- // NOTE: We should consider creating the file in native code atomically once we've
- // gotten rid of the old scheme of dumping and lot of the code that deals with paths
- // can be removed.
- tracesFile = createAnrDumpFile(tracesDir);
- if (tracesFile == null) {
- return null;
- }
-
- useTombstonedForJavaTraces = true;
+ // NOTE: We should consider creating the file in native code atomically once we've
+ // gotten rid of the old scheme of dumping and lot of the code that deals with paths
+ // can be removed.
+ File tracesFile = createAnrDumpFile(tracesDir);
+ if (tracesFile == null) {
+ return null;
}
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,
- useTombstonedForJavaTraces);
+ dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
return tracesFile;
}
@@ -5697,68 +5676,6 @@
}
/**
- * Legacy code, do not use. Existing users will be deleted.
- *
- * @deprecated
- */
- @Deprecated
- public static class DumpStackFileObserver extends FileObserver {
- // Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
- private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
-
- private final String mTracesPath;
- private boolean mClosed;
-
- public DumpStackFileObserver(String tracesPath) {
- super(tracesPath, FileObserver.CLOSE_WRITE);
- mTracesPath = tracesPath;
- }
-
- @Override
- public synchronized void onEvent(int event, String path) {
- mClosed = true;
- notify();
- }
-
- public long dumpWithTimeout(int pid, long timeout) {
- sendSignal(pid, SIGNAL_QUIT);
- final long start = SystemClock.elapsedRealtime();
-
- final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
- synchronized (this) {
- try {
- wait(waitTime); // Wait for traces file to be closed.
- } catch (InterruptedException e) {
- Slog.wtf(TAG, e);
- }
- }
-
- // This avoids a corner case of passing a negative time to the native
- // trace in case we've already hit the overall timeout.
- final long timeWaited = SystemClock.elapsedRealtime() - start;
- if (timeWaited >= timeout) {
- return timeWaited;
- }
-
- if (!mClosed) {
- Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
- ". Attempting native stack collection.");
-
- final long nativeDumpTimeoutMs = Math.min(
- NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
-
- Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
- (int) (nativeDumpTimeoutMs / 1000));
- }
-
- final long end = SystemClock.elapsedRealtime();
- mClosed = false;
-
- return (end - start);
- }
- }
-
- /**
* Dump java traces for process {@code pid} to the specified file. If java trace dumping
* fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
* to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
@@ -5776,106 +5693,78 @@
}
private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
- ArrayList<Integer> nativePids, ArrayList<Integer> extraPids,
- boolean useTombstonedForJavaTraces) {
+ ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
// We don't need any sort of inotify based monitoring when we're dumping traces via
// tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
// control of all writes to the file in question.
- final DumpStackFileObserver observer;
- if (useTombstonedForJavaTraces) {
- observer = null;
- } else {
- // Use a FileObserver to detect when traces finish writing.
- // The order of traces is considered important to maintain for legibility.
- observer = new DumpStackFileObserver(tracesFile);
- }
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
- try {
- if (observer != null) {
- observer.startWatching();
+
+ // First collect all of the stacks of the most important pids.
+ if (firstPids != null) {
+ int num = firstPids.size();
+ for (int i = 0; i < num; i++) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + firstPids.get(i));
+ final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
+ remainingTime);
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+ }
}
+ }
- // First collect all of the stacks of the most important pids.
- if (firstPids != null) {
- int num = firstPids.size();
- for (int i = 0; i < num; i++) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
- + firstPids.get(i));
- final long timeTaken;
- if (useTombstonedForJavaTraces) {
- timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);
- } else {
- timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
- }
+ // Next collect the stacks of the native pids
+ if (nativePids != null) {
+ for (int pid : nativePids) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
+ final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+ final long start = SystemClock.elapsedRealtime();
+ Debug.dumpNativeBacktraceToFileTimeout(
+ pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
+ final long timeTaken = SystemClock.elapsedRealtime() - start;
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+ }
+ }
+ }
+
+ // Lastly, dump stacks for all extra PIDs from the CPU tracker.
+ if (extraPids != null) {
+ for (int pid : extraPids) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
+
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
"); deadline exceeded.");
- return;
- }
-
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
- }
+ return;
}
- }
- // Next collect the stacks of the native pids
- if (nativePids != null) {
- for (int pid : nativePids) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
- final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
-
- final long start = SystemClock.elapsedRealtime();
- Debug.dumpNativeBacktraceToFileTimeout(
- pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
- final long timeTaken = SystemClock.elapsedRealtime() - start;
-
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
- "); deadline exceeded.");
- return;
- }
-
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
- }
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
}
}
-
- // Lastly, dump stacks for all extra PIDs from the CPU tracker.
- if (extraPids != null) {
- for (int pid : extraPids) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
-
- final long timeTaken;
- if (useTombstonedForJavaTraces) {
- timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
- } else {
- timeTaken = observer.dumpWithTimeout(pid, remainingTime);
- }
-
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
- "); deadline exceeded.");
- return;
- }
-
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
- }
- }
- }
- } finally {
- if (observer != null) {
- observer.stopWatching();
- }
}
}
@@ -5922,7 +5811,7 @@
if (app != null) {
ArrayList<Integer> firstPids = new ArrayList<Integer>();
firstPids.add(app.pid);
- dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
+ dumpStackTraces(tracesPath, firstPids, null, null);
}
File lastTracesFile = null;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 36f5a6c..7aaac06 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -34,27 +34,28 @@
import android.net.Network;
import android.net.NetworkUtils;
import android.net.Uri;
+import android.net.dns.ResolvUtil;
import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
-import android.system.GaiException;
-import android.system.OsConstants;
-import android.system.StructAddrinfo;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
import com.android.server.connectivity.MockableSystemProperties;
-import libcore.io.Libcore;
-
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
+import java.util.Set;
import java.util.StringJoiner;
@@ -64,10 +65,56 @@
* This class it NOT designed for concurrent access. Furthermore, all non-static
* methods MUST be called from ConnectivityService's thread.
*
+ * [ Private DNS ]
+ * The code handling Private DNS is spread across several components, but this
+ * seems like the least bad place to collect all the observations.
+ *
+ * Private DNS handling and updating occurs in response to several different
+ * events. Each is described here with its corresponding intended handling.
+ *
+ * [A] Event: A new network comes up.
+ * Mechanics:
+ * [1] ConnectivityService gets notifications from NetworkAgents.
+ * [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
+ * into CONNECTED state, the Private DNS configuration is retrieved,
+ * programmed, and strict mode hostname resolution (if applicable) is
+ * enqueued in NetworkAgent's NetworkMonitor, via a call to
+ * handlePerNetworkPrivateDnsConfig().
+ * [3] Re-resolution of strict mode hostnames that fail to return any
+ * IP addresses happens inside NetworkMonitor; it sends itself a
+ * delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
+ * schedule.
+ * [4] Successfully resolved hostnames are sent to ConnectivityService
+ * inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
+ * IP addresses are programmed into netd via:
+ *
+ * updatePrivateDns() -> updateDnses()
+ *
+ * both of which make calls into DnsManager.
+ * [5] Upon a successful hostname resolution NetworkMonitor initiates a
+ * validation attempt in the form of a lookup for a one-time hostname
+ * that uses Private DNS.
+ *
+ * [B] Event: Private DNS settings are changed.
+ * Mechanics:
+ * [1] ConnectivityService gets notifications from its SettingsObserver.
+ * [2] handlePrivateDnsSettingsChanged() is called, which calls
+ * handlePerNetworkPrivateDnsConfig() and the process proceeds
+ * as if from A.3 above.
+ *
+ * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
+ * Mechanics:
+ * [1] NetworkMonitor is notified and initiates a reevaluation, which
+ * always bypasses Private DNS.
+ * [2] Once completed, NetworkMonitor checks if strict mode is in operation
+ * and if so enqueues another evaluation of Private DNS, as if from
+ * step A.5 above.
+ *
* @hide
*/
public class DnsManager {
private static final String TAG = DnsManager.class.getSimpleName();
+ private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig();
/* Defaults for resolver parameters. */
private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
@@ -126,35 +173,104 @@
}
public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
- final StructAddrinfo hints = new StructAddrinfo();
- // Unnecessary, but expressly no AI_ADDRCONFIG.
- hints.ai_flags = 0;
- // Fetch all IP addresses at once to minimize re-resolution.
- hints.ai_family = OsConstants.AF_UNSPEC;
- hints.ai_socktype = OsConstants.SOCK_DGRAM;
-
try {
- final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
- if (ips != null && ips.length > 0) {
- return new PrivateDnsConfig(name, ips);
- }
- } catch (GaiException ignored) {}
-
- return null;
+ final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(network, name);
+ return new PrivateDnsConfig(name, ips);
+ } catch (UnknownHostException uhe) {
+ return new PrivateDnsConfig(name, null);
+ }
}
public static Uri[] getPrivateDnsSettingsUris() {
- final Uri[] uris = new Uri[2];
- uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
- uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
- return uris;
+ return new Uri[]{
+ Settings.Global.getUriFor(PRIVATE_DNS_MODE),
+ Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
+ };
+ }
+
+ public static class PrivateDnsValidationUpdate {
+ final public int netId;
+ final public InetAddress ipAddress;
+ final public String hostname;
+ final public boolean validated;
+
+ public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
+ String hostname, boolean validated) {
+ this.netId = netId;
+ this.ipAddress = ipAddress;
+ this.hostname = hostname;
+ this.validated = validated;
+ }
+ }
+
+ private static class PrivateDnsValidationStatuses {
+ enum ValidationStatus {
+ IN_PROGRESS,
+ FAILED,
+ SUCCEEDED
+ }
+
+ // Validation statuses of <hostname, ipAddress> pairs for a single netId
+ private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap;
+
+ private PrivateDnsValidationStatuses() {
+ mValidationMap = new HashMap<>();
+ }
+
+ private boolean hasValidatedServer() {
+ for (ValidationStatus status : mValidationMap.values()) {
+ if (status == ValidationStatus.SUCCEEDED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateTrackedDnses(String[] ipAddresses, String hostname) {
+ Set<Pair<String, InetAddress>> latestDnses = new HashSet<>();
+ for (String ipAddress : ipAddresses) {
+ try {
+ latestDnses.add(new Pair(hostname,
+ InetAddress.parseNumericAddress(ipAddress)));
+ } catch (IllegalArgumentException e) {}
+ }
+ // Remove <hostname, ipAddress> pairs that should not be tracked.
+ for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it =
+ mValidationMap.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next();
+ if (!latestDnses.contains(entry.getKey())) {
+ it.remove();
+ }
+ }
+ // Add new <hostname, ipAddress> pairs that should be tracked.
+ for (Pair<String, InetAddress> p : latestDnses) {
+ if (!mValidationMap.containsKey(p)) {
+ mValidationMap.put(p, ValidationStatus.IN_PROGRESS);
+ }
+ }
+ }
+
+ private void updateStatus(PrivateDnsValidationUpdate update) {
+ Pair<String, InetAddress> p = new Pair(update.hostname,
+ update.ipAddress);
+ if (!mValidationMap.containsKey(p)) {
+ return;
+ }
+ if (update.validated) {
+ mValidationMap.put(p, ValidationStatus.SUCCEEDED);
+ } else {
+ mValidationMap.put(p, ValidationStatus.FAILED);
+ }
+ }
}
private final Context mContext;
private final ContentResolver mContentResolver;
private final INetworkManagementService mNMS;
private final MockableSystemProperties mSystemProperties;
+ // TODO: Replace these Maps with SparseArrays.
private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+ private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
private int mNumDnsEntries;
private int mSampleValidity;
@@ -170,6 +286,7 @@
mNMS = nms;
mSystemProperties = sp;
mPrivateDnsMap = new HashMap<>();
+ mPrivateDnsValidationMap = new HashMap<>();
// TODO: Create and register ContentObservers to track every setting
// used herein, posting messages to respond to changes.
@@ -181,6 +298,7 @@
public void removeNetwork(Network network) {
mPrivateDnsMap.remove(network.netId);
+ mPrivateDnsValidationMap.remove(network.netId);
}
public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -190,6 +308,40 @@
: mPrivateDnsMap.remove(network.netId);
}
+ public void updatePrivateDnsStatus(int netId, LinkProperties lp) {
+ // Use the PrivateDnsConfig data pushed to this class instance
+ // from ConnectivityService.
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+ PRIVATE_DNS_OFF);
+
+ final boolean useTls = privateDnsCfg.useTls;
+ final boolean strictMode = privateDnsCfg.inStrictMode();
+ final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+ if (strictMode) {
+ lp.setUsePrivateDns(true);
+ lp.setPrivateDnsServerName(tlsHostname);
+ } else if (useTls) {
+ // We are in opportunistic mode. Private DNS should be used if there
+ // is a known DNS-over-TLS validated server.
+ boolean validated = mPrivateDnsValidationMap.containsKey(netId) &&
+ mPrivateDnsValidationMap.get(netId).hasValidatedServer();
+ lp.setUsePrivateDns(validated);
+ lp.setPrivateDnsServerName(null);
+ } else {
+ // Private DNS is disabled.
+ lp.setUsePrivateDns(false);
+ lp.setPrivateDnsServerName(null);
+ }
+ }
+
+ public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) {
+ final PrivateDnsValidationStatuses statuses =
+ mPrivateDnsValidationMap.get(update.netId);
+ if (statuses == null) return;
+ statuses.updateStatus(update);
+ }
+
public void setDnsConfigurationForNetwork(
int netId, LinkProperties lp, boolean isDefaultNetwork) {
final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
@@ -203,12 +355,13 @@
// NetworkMonitor to decide which networks need validation and runs the
// blocking calls to resolve Private DNS strict mode hostnames.
//
- // At this time we do attempt to enable Private DNS on non-Internet
+ // At this time we do not attempt to enable Private DNS on non-Internet
// networks like IMS.
- final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+ PRIVATE_DNS_OFF);
- final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
- final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+ final boolean useTls = privateDnsCfg.useTls;
+ final boolean strictMode = privateDnsCfg.inStrictMode();
final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
final String[] tlsServers =
strictMode ? NetworkUtils.makeStrings(
@@ -218,6 +371,17 @@
: useTls ? assignedServers // Opportunistic
: new String[0]; // Off
+ // Prepare to track the validation status of the DNS servers in the
+ // resolver config when private DNS is in opportunistic or strict mode.
+ if (useTls) {
+ if (!mPrivateDnsValidationMap.containsKey(netId)) {
+ mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
+ }
+ mPrivateDnsValidationMap.get(netId).updateTrackedDnses(tlsServers, tlsHostname);
+ } else {
+ mPrivateDnsValidationMap.remove(netId);
+ }
+
Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8a2e71c..2845383 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,6 +34,7 @@
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.dns.ResolvUtil;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.metrics.ValidationProbeEvent;
@@ -64,6 +65,7 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -77,6 +79,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Random;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -165,7 +168,7 @@
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
*/
- public static final int CMD_FORCE_REEVALUATION = BASE + 8;
+ private static final int CMD_FORCE_REEVALUATION = BASE + 8;
/**
* Message to self indicating captive portal app finished.
@@ -205,9 +208,15 @@
* Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
* strict mode, then an event is sent back to ConnectivityService with the
* result of the resolution attempt.
+ *
+ * A separate message is used to trigger (re)evaluation of the Private DNS
+ * configuration, so that the message can be handled as needed in different
+ * states, including being ignored until after an ongoing captive portal
+ * validation phase is completed.
*/
private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
+ private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -215,6 +224,7 @@
// Before network has been evaluated this many times, ignore repeated reevaluate requests.
private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
private int mReevaluateToken = 0;
+ private static final int NO_UID = 0;
private static final int INVALID_UID = -1;
private int mUidResponsibleForReeval = INVALID_UID;
// Stop blaming UID that requested re-evaluation after this many attempts.
@@ -224,6 +234,8 @@
private static final int NUM_VALIDATION_LOG_LINES = 20;
+ private String mPrivateDnsProviderHostname = "";
+
public static boolean isValidationRequired(
NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
// TODO: Consider requiring validation for DUN networks.
@@ -261,13 +273,12 @@
public boolean systemReady = false;
- private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null;
-
private final State mDefaultState = new DefaultState();
private final State mValidatedState = new ValidatedState();
private final State mMaybeNotifyState = new MaybeNotifyState();
private final State mEvaluatingState = new EvaluatingState();
private final State mCaptivePortalState = new CaptivePortalState();
+ private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -293,6 +304,10 @@
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + networkAgentInfo.name());
+ // Logs with a tag of the form given just above, e.g.
+ // <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
+ setDbg(VDBG);
+
mContext = context;
mMetricsLog = logger;
mConnectivityServiceHandler = handler;
@@ -305,10 +320,11 @@
mDefaultRequest = defaultRequest;
addState(mDefaultState);
- addState(mValidatedState, mDefaultState);
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mCaptivePortalState, mMaybeNotifyState);
+ addState(mEvaluatingPrivateDnsState, mDefaultState);
+ addState(mValidatedState, mDefaultState);
setInitialState(mDefaultState);
mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
@@ -321,6 +337,17 @@
start();
}
+ public void forceReevaluation(int responsibleUid) {
+ sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
+ }
+
+ public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
+ // Cancel any outstanding resolutions.
+ removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
+ // Send the update to the proper thread.
+ sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
+ }
+
@Override
protected void log(String s) {
if (DBG) Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
@@ -349,6 +376,12 @@
mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
}
+
+ private void notifyNetworkTestResultInvalid(Object obj) {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, obj));
+ }
+
// DefaultState is the parent of all States. It exists only to handle CMD_* messages but
// does not entail any real state (hence no enter() or exit() routines).
private class DefaultState extends State {
@@ -392,41 +425,66 @@
switch (message.arg1) {
case APP_RETURN_DISMISSED:
- sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */, 0);
+ sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0);
break;
case APP_RETURN_WANTED_AS_IS:
mDontDisplaySigninNotification = true;
// TODO: Distinguish this from a network that actually validates.
- // Displaying the "!" on the system UI icon may still be a good idea.
- transitionTo(mValidatedState);
+ // Displaying the "x" on the system UI icon may still be a good idea.
+ transitionTo(mEvaluatingPrivateDnsState);
break;
case APP_RETURN_UNWANTED:
mDontDisplaySigninNotification = true;
mUserDoesNotWant = true;
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
- mNetId, null));
+ notifyNetworkTestResultInvalid(null);
// TODO: Should teardown network.
mUidResponsibleForReeval = 0;
transitionTo(mEvaluatingState);
break;
}
return HANDLED;
- case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
- if (isValidationRequired()) {
- // This performs a blocking DNS resolution of the
- // strict mode hostname, if required.
- resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj);
- if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) {
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId,
- new DnsManager.PrivateDnsConfig(mPrivateDnsCfg)));
- }
+ case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
+ final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
+ if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) {
+ // No DNS resolution required.
+ //
+ // We don't force any validation in opportunistic mode
+ // here. Opportunistic mode nameservers are validated
+ // separately within netd.
+ //
+ // Reset Private DNS settings state.
+ mPrivateDnsProviderHostname = "";
+ break;
}
- return HANDLED;
+
+ mPrivateDnsProviderHostname = cfg.hostname;
+
+ // DNS resolutions via Private DNS strict mode block for a
+ // few seconds (~4.2) checking for any IP addresses to
+ // arrive and validate. Initiating a (re)evaluation now
+ // should not significantly alter the validation outcome.
+ //
+ // No matter what: enqueue a validation request; one of
+ // three things can happen with this request:
+ // [1] ignored (EvaluatingState or CaptivePortalState)
+ // [2] transition to EvaluatingPrivateDnsState
+ // (DefaultState and ValidatedState)
+ // [3] handled (EvaluatingPrivateDnsState)
+ //
+ // The Private DNS configuration to be evaluated will:
+ // [1] be skipped (not in strict mode), or
+ // [2] validate (huzzah), or
+ // [3] encounter some problem (invalid hostname,
+ // no resolved IP addresses, IPs unreachable,
+ // port 853 unreachable, port 853 is not running a
+ // DNS-over-TLS server, et cetera).
+ sendMessage(CMD_EVALUATE_PRIVATE_DNS);
+ break;
+ }
default:
- return HANDLED;
+ break;
}
+ return HANDLED;
}
}
@@ -440,7 +498,7 @@
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg));
+ NETWORK_TEST_RESULT_VALID, mNetId, null));
mValidations++;
}
@@ -449,10 +507,14 @@
switch (message.what) {
case CMD_NETWORK_CONNECTED:
transitionTo(mValidatedState);
- return HANDLED;
+ break;
+ case CMD_EVALUATE_PRIVATE_DNS:
+ transitionTo(mEvaluatingPrivateDnsState);
+ break;
default:
return NOT_HANDLED;
}
+ return HANDLED;
}
}
@@ -569,11 +631,11 @@
case CMD_REEVALUATE:
if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
return HANDLED;
- // Don't bother validating networks that don't satisify the default request.
+ // Don't bother validating networks that don't satisfy the default request.
// This includes:
// - VPNs which can be considered explicitly desired by the user and the
// user's desire trumps whether the network validates.
- // - Networks that don't provide internet access. It's unclear how to
+ // - Networks that don't provide Internet access. It's unclear how to
// validate such networks.
// - Untrusted networks. It's unsafe to prompt the user to sign-in to
// such networks and the user didn't express interest in connecting to
@@ -588,7 +650,6 @@
// expensive metered network, or unwanted leaking of the User Agent string.
if (!isValidationRequired()) {
validationLog("Network would not satisfy default request, not validating");
- mPrivateDnsCfg = null;
transitionTo(mValidatedState);
return HANDLED;
}
@@ -601,20 +662,18 @@
// if this is found to cause problems.
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {
- resolvePrivateDnsConfig();
- transitionTo(mValidatedState);
+ // Transit EvaluatingPrivateDnsState to get to Validated
+ // state (even if no Private DNS validation required).
+ transitionTo(mEvaluatingPrivateDnsState);
} else if (probeResult.isPortal()) {
- mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
+ notifyNetworkTestResultInvalid(probeResult.redirectUrl);
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
- probeResult.redirectUrl));
+ notifyNetworkTestResultInvalid(probeResult.redirectUrl);
if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
// Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
@@ -700,6 +759,110 @@
}
}
+ private class EvaluatingPrivateDnsState extends State {
+ private int mPrivateDnsReevalDelayMs;
+ private PrivateDnsConfig mPrivateDnsConfig;
+
+ @Override
+ public void enter() {
+ mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
+ mPrivateDnsConfig = null;
+ sendMessage(CMD_EVALUATE_PRIVATE_DNS);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_EVALUATE_PRIVATE_DNS:
+ if (inStrictMode()) {
+ if (!isStrictModeHostnameResolved()) {
+ resolveStrictModeHostname();
+
+ if (isStrictModeHostnameResolved()) {
+ notifyPrivateDnsConfigResolved();
+ } else {
+ handlePrivateDnsEvaluationFailure();
+ break;
+ }
+ }
+
+ // Look up a one-time hostname, to bypass caching.
+ //
+ // Note that this will race with ConnectivityService
+ // code programming the DNS-over-TLS server IP addresses
+ // into netd (if invoked, above). If netd doesn't know
+ // the IP addresses yet, or if the connections to the IP
+ // addresses haven't yet been validated, netd will block
+ // for up to a few seconds before failing the lookup.
+ if (!sendPrivateDnsProbe()) {
+ handlePrivateDnsEvaluationFailure();
+ break;
+ }
+ }
+
+ // All good!
+ transitionTo(mValidatedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ private boolean inStrictMode() {
+ return !TextUtils.isEmpty(mPrivateDnsProviderHostname);
+ }
+
+ private boolean isStrictModeHostnameResolved() {
+ return (mPrivateDnsConfig != null) &&
+ mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
+ (mPrivateDnsConfig.ips.length > 0);
+ }
+
+ private void resolveStrictModeHostname() {
+ try {
+ // Do a blocking DNS resolution using the network-assigned nameservers.
+ mPrivateDnsConfig = new PrivateDnsConfig(
+ mPrivateDnsProviderHostname,
+ mNetwork.getAllByName(mPrivateDnsProviderHostname));
+ } catch (UnknownHostException uhe) {
+ mPrivateDnsConfig = null;
+ }
+ }
+
+ private void notifyPrivateDnsConfigResolved() {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, mPrivateDnsConfig));
+ }
+
+ private void handlePrivateDnsEvaluationFailure() {
+ notifyNetworkTestResultInvalid(null);
+
+ // Queue up a re-evaluation with backoff.
+ //
+ // TODO: Consider abandoning this state after a few attempts and
+ // transitioning back to EvaluatingState, to perhaps give ourselves
+ // the opportunity to (re)detect a captive portal or something.
+ sendMessageDelayed(CMD_EVALUATE_PRIVATE_DNS, mPrivateDnsReevalDelayMs);
+ mPrivateDnsReevalDelayMs *= 2;
+ if (mPrivateDnsReevalDelayMs > MAX_REEVALUATE_DELAY_MS) {
+ mPrivateDnsReevalDelayMs = MAX_REEVALUATE_DELAY_MS;
+ }
+ }
+
+ private boolean sendPrivateDnsProbe() {
+ // q.v. system/netd/server/dns/DnsTlsTransport.cpp
+ final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
+ final String host = UUID.randomUUID().toString().substring(0, 8) +
+ ONE_TIME_HOSTNAME_SUFFIX;
+ try {
+ final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
+ return (ips != null && ips.length > 0);
+ } catch (UnknownHostException uhe) {}
+ return false;
+ }
+ }
+
// Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
// most one per address family. This ensures we only wait up to 20 seconds for TCP connections
// to complete, regardless of how many IP addresses a host has.
@@ -710,7 +873,9 @@
@Override
public InetAddress[] getAllByName(String host) throws UnknownHostException {
- List<InetAddress> addrs = Arrays.asList(super.getAllByName(host));
+ // Always bypass Private DNS.
+ final List<InetAddress> addrs = Arrays.asList(
+ ResolvUtil.blockingResolveAllLocally(this, host));
// Ensure the address family of the first address is tried first.
LinkedHashMap<Class, InetAddress> addressByFamily = new LinkedHashMap<>();
@@ -1065,44 +1230,6 @@
return null;
}
- public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) {
- // Cancel any outstanding resolutions.
- removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
- // Send the update to the proper thread.
- sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
- }
-
- private void resolvePrivateDnsConfig() {
- resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver()));
- }
-
- private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) {
- // Nothing to do.
- if (cfg == null) {
- mPrivateDnsCfg = null;
- return;
- }
-
- // No DNS resolution required.
- if (!cfg.inStrictMode()) {
- mPrivateDnsCfg = cfg;
- return;
- }
-
- if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() &&
- (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) {
- // We have already resolved this strict mode hostname. Assume that
- // Private DNS services won't be changing serving IP addresses very
- // frequently and save ourselves one re-resolve.
- return;
- }
-
- mPrivateDnsCfg = cfg;
- final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf(
- mNetwork, mPrivateDnsCfg.hostname);
- if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg;
- }
-
/**
* @param responseReceived - whether or not we received a valid HTTP response to our request.
* If false, isCaptivePortal and responseTimestampMs are ignored
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index cffa834..62fe218 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,27 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_INVALID;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -45,7 +66,6 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.IpPrefix;
@@ -56,6 +76,7 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.util.VersionedBroadcastListener;
@@ -98,6 +119,7 @@
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import com.android.server.net.BaseNetworkObserver;
@@ -146,7 +168,7 @@
stateMachine = sm;
// Assume all state machines start out available and with no errors.
lastState = IControlsTethering.STATE_AVAILABLE;
- lastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ lastError = TETHER_ERROR_NO_ERROR;
}
public boolean isCurrentlyServing() {
@@ -181,9 +203,10 @@
private final VersionedBroadcastListener mCarrierConfigChange;
// TODO: Delete SimChangeListener; it's obsolete.
private final SimChangeListener mSimChange;
+ private final TetheringDependencies mDeps;
private volatile TetheringConfiguration mConfig;
- private String mCurrentUpstreamIface;
+ private InterfaceSet mCurrentUpstreamIfaceSet;
private Notification.Builder mTetheredNotificationBuilder;
private int mLastNotificationId;
@@ -202,21 +225,22 @@
mPolicyManager = policyManager;
mLooper = looper;
mSystemProperties = systemProperties;
+ mDeps = deps;
mPublicSync = new Object();
mTetherStates = new ArrayMap<>();
- mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
+ mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
final Handler smHandler = mTetherMasterSM.getHandler();
mOffloadController = new OffloadController(smHandler,
- deps.getOffloadHardwareInterface(smHandler, mLog),
+ mDeps.getOffloadHardwareInterface(smHandler, mLog),
mContext.getContentResolver(), mNMService,
mLog);
- mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
- mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+ mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
+ TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
IntentFilter filter = new IntentFilter();
@@ -225,6 +249,7 @@
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
+ updateConfiguration();
reevaluateSimCardProvisioning();
});
// TODO: Remove SimChangeListener altogether. For now, we retain it
@@ -237,34 +262,35 @@
});
mStateReceiver = new StateReceiver();
- filter = new IntentFilter();
+
+ // Load tethering configuration.
+ updateConfiguration();
+
+ startStateMachineUpdaters();
+ }
+
+ private void startStateMachineUpdaters() {
+ mCarrierConfigChange.startListening();
+
+ final Handler handler = mTetherMasterSM.getHandler();
+ IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
- UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
-
- // this check is useful only for some unit tests; example: ConnectivityServiceTest
- if (userManager != null) {
- userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ // This check is useful only for some unit tests; example: ConnectivityServiceTest.
+ if (umi != null) {
+ umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
}
-
- // load device config info
- updateConfiguration();
- }
-
- // We can't do this once in the Tethering() constructor and cache the value, because the
- // CONNECTIVITY_SERVICE is registered only after the Tethering() constructor has completed.
- private ConnectivityManager getConnectivityManager() {
- return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
private WifiManager getWifiManager() {
@@ -292,7 +318,7 @@
if (up) {
maybeTrackNewInterfaceLocked(iface);
} else {
- if (ifaceNameToType(iface) == ConnectivityManager.TETHERING_BLUETOOTH) {
+ if (ifaceNameToType(iface) == TETHERING_BLUETOOTH) {
stopTrackingInterfaceLocked(iface);
} else {
// Ignore usb0 down after enabling RNDIS.
@@ -314,13 +340,13 @@
final TetheringConfiguration cfg = mConfig;
if (cfg.isWifi(iface)) {
- return ConnectivityManager.TETHERING_WIFI;
+ return TETHERING_WIFI;
} else if (cfg.isUsb(iface)) {
- return ConnectivityManager.TETHERING_USB;
+ return TETHERING_USB;
} else if (cfg.isBluetooth(iface)) {
- return ConnectivityManager.TETHERING_BLUETOOTH;
+ return TETHERING_BLUETOOTH;
}
- return ConnectivityManager.TETHERING_INVALID;
+ return TETHERING_INVALID;
}
@Override
@@ -366,17 +392,15 @@
*/
@VisibleForTesting
protected boolean isTetherProvisioningRequired() {
- String[] provisionApp = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ final TetheringConfiguration cfg = mConfig;
if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || provisionApp == null) {
+ || cfg.provisioningApp.length == 0) {
return false;
}
-
if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
return false;
}
- return (provisionApp.length == 2);
+ return (cfg.provisioningApp.length == 2);
}
// The logic here is aimed solely at confirming that a CarrierConfig exists
@@ -399,20 +423,6 @@
return !isEntitlementCheckRequired;
}
- // Used by the SIM card change observation code.
- // TODO: De-duplicate above code.
- private boolean hasMobileHotspotProvisionApp() {
- try {
- if (!mContext.getResources().getString(com.android.internal.R.string.
- config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
- Log.d(TAG, "re-evaluate provisioning");
- return true;
- }
- } catch (Resources.NotFoundException e) {}
- Log.d(TAG, "no prov-check needed for new SIM");
- return false;
- }
-
/**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
@@ -422,26 +432,26 @@
boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
int result;
switch (type) {
- case ConnectivityManager.TETHERING_WIFI:
+ case TETHERING_WIFI:
result = setWifiTethering(enable);
- if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
- case ConnectivityManager.TETHERING_USB:
+ case TETHERING_USB:
result = setUsbTethering(enable);
- if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
- case ConnectivityManager.TETHERING_BLUETOOTH:
+ case TETHERING_BLUETOOTH:
setBluetoothTethering(enable, receiver);
break;
default:
Log.w(TAG, "Invalid tether type.");
- sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE);
+ sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
}
}
@@ -452,7 +462,7 @@
}
private int setWifiTethering(final boolean enable) {
- int rval = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ int rval = TETHER_ERROR_MASTER_ERROR;
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mPublicSync) {
@@ -460,7 +470,7 @@
final WifiManager mgr = getWifiManager();
if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
(!enable && mgr.stopSoftAp())) {
- rval = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ rval = TETHER_ERROR_NO_ERROR;
}
}
} finally {
@@ -474,7 +484,7 @@
if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
(adapter == null));
- sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL);
+ sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL);
return;
}
@@ -487,12 +497,12 @@
((BluetoothPan) proxy).setBluetoothTethering(enable);
// TODO: Enabling bluetooth tethering can fail asynchronously here.
// We should figure out a way to bubble up that failure instead of sending success.
- int result = ((BluetoothPan) proxy).isTetheringOn() == enable ?
- ConnectivityManager.TETHER_ERROR_NO_ERROR :
- ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
+ ? TETHER_ERROR_NO_ERROR
+ : TETHER_ERROR_MASTER_ERROR;
sendTetherResult(receiver, result);
if (enable && isTetherProvisioningRequired()) {
- scheduleProvisioningRechecks(ConnectivityManager.TETHERING_BLUETOOTH);
+ scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
}
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
@@ -506,8 +516,8 @@
private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
- intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final long ident = Binder.clearCallingIdentity();
try {
@@ -530,7 +540,7 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// If provisioning is successful, enable tethering, otherwise just send the error.
- if (resultCode == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
enableTetheringInternal(type, true, receiver);
} else {
sendTetherResult(receiver, resultCode);
@@ -550,8 +560,8 @@
private void scheduleProvisioningRechecks(int type) {
Intent intent = new Intent();
- intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(ConnectivityManager.EXTRA_SET_ALARM, true);
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_SET_ALARM, true);
intent.setComponent(TETHER_SERVICE);
final long ident = Binder.clearCallingIdentity();
try {
@@ -568,9 +578,9 @@
private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
Intent intent = new Intent();
- intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
- intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.setComponent(TETHER_SERVICE);
final long ident = Binder.clearCallingIdentity();
try {
@@ -581,9 +591,9 @@
}
private void cancelTetherProvisioningRechecks(int type) {
- if (getConnectivityManager().isTetheringSupported()) {
+ if (mDeps.isTetheringSupported()) {
Intent intent = new Intent();
- intent.putExtra(ConnectivityManager.EXTRA_REM_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
intent.setComponent(TETHER_SERVICE);
final long ident = Binder.clearCallingIdentity();
try {
@@ -598,8 +608,8 @@
// TODO: De-duplicate with above code, where possible.
private void startProvisionIntent(int tetherType) {
final Intent startProvIntent = new Intent();
- startProvIntent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
startProvIntent.setComponent(TETHER_SERVICE);
mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
}
@@ -614,13 +624,13 @@
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
// Ignore the error status of the interface. If the interface is available,
// the errors are referring to past tethering attempts anyway.
if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+ return TETHER_ERROR_UNAVAIL_IFACE;
}
// NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's
// queue but not yet processed, this will be a no-op and it will not
@@ -629,7 +639,7 @@
// TODO: reexamine the threading and messaging model.
tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
- return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ return TETHER_ERROR_NO_ERROR;
}
}
@@ -639,22 +649,22 @@
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
if (!tetherState.isCurrentlyServing()) {
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+ return TETHER_ERROR_UNAVAIL_IFACE;
}
tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ return TETHER_ERROR_NO_ERROR;
}
}
public void untetherAll() {
- stopTethering(ConnectivityManager.TETHERING_WIFI);
- stopTethering(ConnectivityManager.TETHERING_USB);
- stopTethering(ConnectivityManager.TETHERING_BLUETOOTH);
+ stopTethering(TETHERING_WIFI);
+ stopTethering(TETHERING_USB);
+ stopTethering(TETHERING_BLUETOOTH);
}
public int getLastTetherError(String iface) {
@@ -663,7 +673,7 @@
if (tetherState == null) {
Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
", ignoring");
- return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
return tetherState.lastError;
}
@@ -671,7 +681,7 @@
// TODO: Figure out how to update for local hotspot mode interfaces.
private void sendTetherStateChangedBroadcast() {
- if (!getConnectivityManager().isTetheringSupported()) return;
+ if (!mDeps.isTetheringSupported()) return;
final ArrayList<String> availableList = new ArrayList<>();
final ArrayList<String> tetherList = new ArrayList<>();
@@ -688,7 +698,7 @@
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
String iface = mTetherStates.keyAt(i);
- if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
} else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
availableList.add(iface);
@@ -706,13 +716,13 @@
}
}
}
- final Intent bcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER, availableList);
- bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
- bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, tetherList);
- bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER, erroredList);
+ bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
+ bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
+ bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
+ bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList);
mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
if (DBG) {
Log.d(TAG, String.format(
@@ -835,7 +845,7 @@
if (action.equals(UsbManager.ACTION_USB_STATE)) {
handleUsbAction(intent);
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ } else if (action.equals(CONNECTIVITY_ACTION)) {
handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
handleWifiApAction(intent);
@@ -846,8 +856,8 @@
}
private void handleConnectivityAction(Intent intent) {
- final NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
+ final NetworkInfo networkInfo =
+ (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO);
if (networkInfo == null ||
networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
return;
@@ -883,14 +893,10 @@
synchronized (Tethering.this.mPublicSync) {
if (!usbConnected && mRndisEnabled) {
// Turn off tethering if it was enabled and there is a disconnect.
- tetherMatchingInterfaces(
- IControlsTethering.STATE_AVAILABLE,
- ConnectivityManager.TETHERING_USB);
+ tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
- tetherMatchingInterfaces(
- IControlsTethering.STATE_TETHERED,
- ConnectivityManager.TETHERING_USB);
+ tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
}
mRndisEnabled = usbConfigured && rndisEnabled;
}
@@ -971,7 +977,7 @@
for (int i = 0; i < mTetherStates.size(); i++) {
TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
- if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+ if (tism.interfaceType() == TETHERING_WIFI) {
tism.unwanted();
return;
}
@@ -999,7 +1005,7 @@
}
if (!TextUtils.isEmpty(ifname)) {
- maybeTrackNewInterfaceLocked(ifname, ConnectivityManager.TETHERING_WIFI);
+ maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
@@ -1058,7 +1064,7 @@
Log.wtf(TAG, "Unknown interface state: " + requestedState);
return;
}
- if (result != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (result != TETHER_ERROR_NO_ERROR) {
Log.e(TAG, "unable start or stop tethering on iface " + ifname);
return;
}
@@ -1099,7 +1105,7 @@
synchronized (mPublicSync) {
usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
}
- return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ return TETHER_ERROR_NO_ERROR;
}
// TODO review API - figure out how to delete these entirely.
@@ -1138,7 +1144,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1159,12 +1165,11 @@
}
// Needed because the canonical source of upstream truth is just the
- // upstream interface name, |mCurrentUpstreamIface|. This is ripe for
- // future simplification, once the upstream Network is canonical.
+ // upstream interface set, |mCurrentUpstreamIfaceSet|.
private boolean pertainsToCurrentUpstream(NetworkState ns) {
- if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
+ if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
- if (mCurrentUpstreamIface.equals(ifname)) {
+ if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
return true;
}
}
@@ -1173,7 +1178,7 @@
}
private void reevaluateSimCardProvisioning() {
- if (!hasMobileHotspotProvisionApp()) return;
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
ArrayList<Integer> tethered = new ArrayList<>();
@@ -1185,7 +1190,7 @@
}
String iface = mTetherStates.keyAt(i);
int interfaceType = ifaceNameToType(iface);
- if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+ if (interfaceType != TETHERING_INVALID) {
tethered.add(interfaceType);
}
}
@@ -1240,7 +1245,7 @@
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
- TetherMasterSM(String name, Looper looper) {
+ TetherMasterSM(String name, Looper looper, TetheringDependencies deps) {
super(name, looper);
mInitialState = new InitialState();
@@ -1260,7 +1265,7 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+ mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
setInitialState(mInitialState);
@@ -1359,31 +1364,27 @@
}
protected void setUpstreamNetwork(NetworkState ns) {
- String iface = null;
+ InterfaceSet ifaces = null;
if (ns != null) {
// Find the interface with the default IPv4 route. It may be the
// interface described by linkProperties, or one of the interfaces
// stacked on top of it.
mLog.i("Looking for default routes on: " + ns.linkProperties);
- final String iface4 = getIPv4DefaultRouteInterface(ns);
- final String iface6 = getIPv6DefaultRouteInterface(ns);
- mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
-
- iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
+ ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
+ mLog.i("Found upstream interface(s): " + ifaces);
}
- if (iface != null) {
+ if (ifaces != null) {
setDnsForwarders(ns.network, ns.linkProperties);
}
- notifyDownstreamsOfNewUpstreamIface(iface);
+ notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
// If we already have NetworkState for this network examine
// it immediately, because there likely will be no second
// EVENT_ON_AVAILABLE (it was already received).
handleNewUpstreamNetworkState(ns);
- } else if (mCurrentUpstreamIface == null) {
- // There are no available upstream networks, or none that
- // have an IPv4 default route (current metric for success).
+ } else if (mCurrentUpstreamIfaceSet == null) {
+ // There are no available upstream networks.
handleNewUpstreamNetworkState(null);
}
}
@@ -1410,12 +1411,10 @@
}
}
- protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
- mLog.log("Notifying downstreams of upstream=" + ifaceName);
- mCurrentUpstreamIface = ifaceName;
+ protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
+ mCurrentUpstreamIfaceSet = ifaces;
for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- ifaceName);
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
}
}
@@ -1441,7 +1440,7 @@
}
// If this is a Wi-Fi interface, notify WifiManager of the active serving state.
- if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+ if (who.interfaceType() == TETHERING_WIFI) {
final WifiManager mgr = getWifiManager();
final String iface = who.interfaceName();
switch (mode) {
@@ -1465,8 +1464,8 @@
mForwardedDownstreams.remove(who);
// If this is a Wi-Fi interface, tell WifiManager of any errors.
- if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
- if (who.lastError() != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (who.interfaceType() == TETHERING_WIFI) {
+ if (who.lastError() != TETHER_ERROR_NO_ERROR) {
getWifiManager().updateInterfaceIpState(
who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
}
@@ -1487,7 +1486,7 @@
// For example, after CONNECTIVITY_ACTION listening is removed, here
// is where we could observe a Wi-Fi network becoming available and
// passing validation.
- if (mCurrentUpstreamIface == null) {
+ if (mCurrentUpstreamIfaceSet == null) {
// If we have no upstream interface, try to run through upstream
// selection again. If, for example, IPv4 connectivity has shown up
// after IPv6 (e.g., 464xlat became available) we want the chance to
@@ -1511,8 +1510,7 @@
handleNewUpstreamNetworkState(ns);
break;
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
- setDnsForwarders(ns.network, ns.linkProperties);
- handleNewUpstreamNetworkState(ns);
+ chooseUpstreamType(false);
break;
case UpstreamNetworkMonitor.EVENT_ON_LOST:
// TODO: Re-evaluate possible upstreams. Currently upstream
@@ -1539,7 +1537,6 @@
return;
}
- mCarrierConfigChange.startListening();
mSimChange.startListening();
mUpstreamNetworkMonitor.start();
@@ -1557,7 +1554,6 @@
mOffload.stop();
mUpstreamNetworkMonitor.stop();
mSimChange.stopListening();
- mCarrierConfigChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
@@ -1585,7 +1581,7 @@
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- mCurrentUpstreamIface);
+ mCurrentUpstreamIfaceSet);
// If there has been a change and an upstream is now
// desired, kick off the selection process.
final boolean previousUpstreamWanted = updateUpstreamWanted();
@@ -1672,7 +1668,7 @@
who.sendMessage(mErrorNotification);
break;
case CMD_CLEAR_ERROR:
- mErrorNotification = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ mErrorNotification = TETHER_ERROR_NO_ERROR;
transitionTo(mInitialState);
break;
default:
@@ -1863,7 +1859,7 @@
pw.println(" - lastError = " + tetherState.lastError);
}
pw.println("Upstream wanted: " + upstreamWanted());
- pw.println("Current upstream interface: " + mCurrentUpstreamIface);
+ pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
pw.decreaseIndent();
}
@@ -1934,7 +1930,7 @@
// If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
// Thus we give a chance for TetherMasterSM to recover to InitialState
// by sending CMD_CLEAR_ERROR
- if (error == ConnectivityManager.TETHER_ERROR_MASTER_ERROR) {
+ if (error == TETHER_ERROR_MASTER_ERROR) {
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
}
int which;
@@ -1978,7 +1974,7 @@
private void maybeTrackNewInterfaceLocked(final String iface) {
// If we don't care about this type of interface, ignore.
final int interfaceType = ifaceNameToType(iface);
- if (interfaceType == ConnectivityManager.TETHERING_INVALID) {
+ if (interfaceType == TETHERING_INVALID) {
mLog.log(iface + " is not a tetherable iface, ignoring");
return;
}
@@ -1996,7 +1992,7 @@
final TetherState tetherState = new TetherState(
new TetherInterfaceStateMachine(
iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
- makeControlCallback(iface)));
+ makeControlCallback(iface), mDeps));
mTetherStates.put(iface, tetherState);
tetherState.stateMachine.start();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c9bdcf1..2fda08e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,6 +151,13 @@
.multiply(BigInteger.valueOf(howManyPercentIsMost))
.divide(BigInteger.valueOf(100));
}
+ // How many routes to evaluate before bailing and declaring this Vpn should provide
+ // the INTERNET capability. This is necessary because computing the adress space is
+ // O(n²) and this is running in the system service, so a limit is needed to alleviate
+ // the risk of attack.
+ // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
+ // is actually O(n²)+O(n²).
+ private static final int MAX_ROUTES_TO_EVALUATE = 150;
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -862,10 +869,12 @@
*/
@VisibleForTesting
static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+ final List<RouteInfo> routes = lp.getAllRoutes();
+ if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
- for (final RouteInfo route : lp.getAllRoutes()) {
+ for (final RouteInfo route : routes) {
IpPrefix destination = route.getDestination();
if (destination.isIPv4()) {
ipv4Prefixes.add(destination);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 518f6c1..ba67c94 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -30,10 +30,8 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;
@@ -119,7 +117,7 @@
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
- if (!canTetherIPv6(ns, mLog)) {
+ if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -208,70 +206,6 @@
return null;
}
- private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
- // Broadly speaking:
- //
- // [1] does the upstream have an IPv6 default route?
- //
- // and
- //
- // [2] does the upstream have one or more global IPv6 /64s
- // dedicated to this device?
- //
- // In lieu of Prefix Delegation and other evaluation of whether a
- // prefix may or may not be dedicated to this device, for now just
- // check whether the upstream is TRANSPORT_CELLULAR. This works
- // because "[t]he 3GPP network allocates each default bearer a unique
- // /64 prefix", per RFC 6459, Section 5.2.
-
- final boolean canTether =
- (ns != null) && (ns.network != null) &&
- (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
- // At least one upstream DNS server:
- ns.linkProperties.isProvisioned() &&
- // Minimal amount of IPv6 provisioning:
- ns.linkProperties.hasIPv6DefaultRoute() &&
- ns.linkProperties.hasGlobalIPv6Address() &&
- // Temporary approximation of "dedicated prefix":
- ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
- // tethering with 464xlat involved). TODO: Rectify this shortcoming,
- // likely by calling NetworkManagementService#startInterfaceForwarding()
- // for all upstream interfaces.
- RouteInfo v4default = null;
- RouteInfo v6default = null;
- if (canTether) {
- for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
- if (r.isIPv4Default()) {
- v4default = r;
- } else if (r.isIPv6Default()) {
- v6default = r;
- }
-
- if (v4default != null && v6default != null) {
- break;
- }
- }
- }
-
- final boolean supportedConfiguration =
- (v4default != null) && (v6default != null) &&
- (v4default.getInterface() != null) &&
- v4default.getInterface().equals(v6default.getInterface());
-
- final boolean outcome = canTether && supportedConfiguration;
-
- if (ns == null) {
- sharedLog.log("No available upstream.");
- } else {
- sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
- (outcome ? "available" : "not available"), toDebugString(ns)));
- }
-
- return outcome;
- }
-
private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
final LinkProperties v6only = new LinkProperties();
if (lp == null) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..5ed14a0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -31,7 +31,7 @@
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
+import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -49,12 +49,12 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Random;
+import java.util.Set;
/**
* Provides the interface to IP-layer serving functionality for a given network
@@ -117,9 +117,11 @@
private final int mInterfaceType;
private final LinkProperties mLinkProperties;
+ private final TetheringDependencies mDeps;
+
private int mLastError;
private int mServingMode;
- private String mMyUpstreamIfaceName; // may change over time
+ private InterfaceSet mUpstreamIfaceSet; // may change over time
private InterfaceParams mInterfaceParams;
// TODO: De-duplicate this with mLinkProperties above. Currently, these link
// properties are those selected by the IPv6TetheringCoordinator and relayed
@@ -134,18 +136,19 @@
public TetherInterfaceStateMachine(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
- IControlsTethering tetherController) {
+ IControlsTethering tetherController,
+ TetheringDependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
- // TODO: This should be passed in for testability.
- mNetd = NetdService.getInstance();
+ mNetd = deps.getNetdService();
mStatsService = statsService;
mTetherController = tetherController;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
+ mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mServingMode = IControlsTethering.STATE_AVAILABLE;
@@ -246,16 +249,14 @@
}
private boolean startIPv6() {
- // TODO: Refactor for better testability. This is one of the things
- // that prohibits unittesting IPv6 tethering setup.
- mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+ mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
if (mInterfaceParams == null) {
mLog.e("Failed to find InterfaceParams");
stopIPv6();
return false;
}
- mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
+ mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
if (!mRaDaemon.start()) {
stopIPv6();
return false;
@@ -621,10 +622,10 @@
}
private void cleanupUpstream() {
- if (mMyUpstreamIfaceName == null) return;
+ if (mUpstreamIfaceSet == null) return;
- cleanupUpstreamInterface(mMyUpstreamIfaceName);
- mMyUpstreamIfaceName = null;
+ for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
+ mUpstreamIfaceSet = null;
}
private void cleanupUpstreamInterface(String upstreamIface) {
@@ -660,34 +661,66 @@
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
break;
case CMD_TETHER_CONNECTION_CHANGED:
- String newUpstreamIfaceName = (String)(message.obj);
- if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
- (mMyUpstreamIfaceName != null &&
- mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+ final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
+ if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
break;
}
- cleanupUpstream();
- if (newUpstreamIfaceName != null) {
+
+ if (newUpstreamIfaceSet == null) {
+ cleanupUpstream();
+ break;
+ }
+
+ for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
+ cleanupUpstreamInterface(removed);
+ }
+
+ final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
+ // This makes the call to cleanupUpstream() in the error
+ // path for any interface neatly cleanup all the interfaces.
+ mUpstreamIfaceSet = newUpstreamIfaceSet;
+
+ for (String ifname : added) {
try {
- mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
- mNMService.startInterfaceForwarding(mIfaceName,
- newUpstreamIfaceName);
+ mNMService.enableNat(mIfaceName, ifname);
+ mNMService.startInterfaceForwarding(mIfaceName, ifname);
} catch (Exception e) {
mLog.e("Exception enabling NAT: " + e);
- cleanupUpstreamInterface(newUpstreamIfaceName);
+ cleanupUpstream();
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
transitionTo(mInitialState);
return true;
}
}
- mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
default:
return false;
}
return true;
}
+
+ private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
+ if (mUpstreamIfaceSet == null && newIfaces == null) return true;
+ if (mUpstreamIfaceSet != null && newIfaces != null) {
+ return mUpstreamIfaceSet.equals(newIfaces);
+ }
+ return false;
+ }
+
+ private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
+ if (mUpstreamIfaceSet == null) return new HashSet<>();
+
+ final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
+ removed.removeAll(newIfaces.ifnames);
+ return removed;
+ }
+
+ private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
+ final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
+ if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
+ return added;
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 09bce7f..454c579 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,14 +21,23 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.telephony.TelephonyManager;
import android.net.util.SharedLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,6 +60,8 @@
public class TetheringConfiguration {
private static final String TAG = TetheringConfiguration.class.getSimpleName();
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
@VisibleForTesting
public static final int DUN_NOT_REQUIRED = 0;
public static final int DUN_REQUIRED = 1;
@@ -79,18 +90,18 @@
public final String[] dhcpRanges;
public final String[] defaultIPv4DNS;
+ public final String[] provisioningApp;
+ public final String provisioningAppNoUi;
+
public TetheringConfiguration(Context ctx, SharedLog log) {
final SharedLog configLog = log.forSubComponent("config");
- tetherableUsbRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs);
+ tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
- tetherableBluetoothRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
@@ -101,6 +112,9 @@
dhcpRanges = getDhcpRanges(ctx);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
+ provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+
configLog.log(toString());
}
@@ -116,6 +130,10 @@
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
+ public boolean hasMobileHotspotProvisionApp() {
+ return !TextUtils.isEmpty(provisioningAppNoUi);
+ }
+
public void dump(PrintWriter pw) {
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -129,6 +147,10 @@
dumpStringArray(pw, "dhcpRanges", dhcpRanges);
dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
+
+ dumpStringArray(pw, "provisioningApp", provisioningApp);
+ pw.print("provisioningAppNoUi: ");
+ pw.println(provisioningAppNoUi);
}
public String toString() {
@@ -140,6 +162,8 @@
sj.add(String.format("isDunRequired:%s", isDunRequired));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+ sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
+ sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
return String.format("TetheringConfiguration{%s}", sj.toString());
}
@@ -159,6 +183,7 @@
}
private static String makeString(String[] strings) {
+ if (strings == null) return "null";
final StringJoiner sj = new StringJoiner(",", "[", "]");
for (String s : strings) sj.add(s);
return sj.toString();
@@ -195,8 +220,7 @@
}
private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
- final int ifaceTypes[] = ctx.getResources().getIntArray(
- com.android.internal.R.array.config_tether_upstream_types);
+ final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -247,14 +271,30 @@
}
private static String[] getDhcpRanges(Context ctx) {
- final String[] fromResource = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range);
+ final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
return copy(DHCP_DEFAULT_RANGE);
}
+ private static String getProvisioningAppNoUi(Context ctx) {
+ try {
+ return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+ } catch (Resources.NotFoundException e) {
+ return "";
+ }
+ }
+
+ private static String[] getResourceStringArray(Context ctx, int resId) {
+ try {
+ final String[] strArray = ctx.getResources().getStringArray(resId);
+ return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
+ } catch (Resources.NotFoundException e404) {
+ return EMPTY_STRING_ARRAY;
+ }
+ }
+
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index b8174b6..0ac7a36 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,9 +16,18 @@
package com.android.server.connectivity.tethering;
+import android.content.Context;
+import android.net.INetd;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetdService;
import android.os.Handler;
import android.net.util.SharedLog;
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+
/**
* Capture tethering dependencies, for injection.
@@ -29,4 +38,30 @@
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
+
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
+ SharedLog log, int what) {
+ return new UpstreamNetworkMonitor(ctx, target, log, what);
+ }
+
+ public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ return new IPv6TetheringCoordinator(notifyList, log);
+ }
+
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return new RouterAdvertisementDaemon(ifParams);
+ }
+
+ public InterfaceParams getInterfaceParams(String ifName) {
+ return InterfaceParams.getByName(ifName);
+ }
+
+ public INetd getNetdService() {
+ return NetdService.getInstance();
+ }
+
+ public boolean isTetheringSupported() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
new file mode 100644
index 0000000..6c7ff91
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.server.connectivity.tethering;
+
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+/**
+ * @hide
+ */
+public final class TetheringInterfaceUtils {
+ /**
+ * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
+ * @return null if there is no usable interface, or a set of at least one interface otherwise.
+ */
+ public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
+ if (ns == null) {
+ return null;
+ }
+
+ final LinkProperties lp = ns.linkProperties;
+ final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
+ final String if6 = getIPv6Interface(ns);
+
+ return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
+ }
+
+ /**
+ * Get the upstream interface for IPv6 tethering.
+ * @return null if there is no usable interface, or the interface name otherwise.
+ */
+ public static @Nullable String getIPv6Interface(NetworkState ns) {
+ // Broadly speaking:
+ //
+ // [1] does the upstream have an IPv6 default route?
+ //
+ // and
+ //
+ // [2] does the upstream have one or more global IPv6 /64s
+ // dedicated to this device?
+ //
+ // In lieu of Prefix Delegation and other evaluation of whether a
+ // prefix may or may not be dedicated to this device, for now just
+ // check whether the upstream is TRANSPORT_CELLULAR. This works
+ // because "[t]he 3GPP network allocates each default bearer a unique
+ // /64 prefix", per RFC 6459, Section 5.2.
+ final boolean canTether =
+ (ns != null) && (ns.network != null) &&
+ (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+ // At least one upstream DNS server:
+ ns.linkProperties.hasIPv6DnsServer() &&
+ // Minimal amount of IPv6 provisioning:
+ ns.linkProperties.hasGlobalIPv6Address() &&
+ // Temporary approximation of "dedicated prefix":
+ ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+ return canTether
+ ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
+ : null;
+ }
+
+ private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
+ final RouteInfo ri = (lp != null)
+ ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
+ : null;
+ return (ri != null) ? ri.getInterface() : null;
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e0a8643..da40692 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -146,7 +146,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
/**
* Collect and persist detailed network statistics, and provide this data to
@@ -756,7 +755,7 @@
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try {
final String[] ifacesToQuery =
- NetworkStatsFactory.augmentWithStackedInterfacesLocked(requiredIfaces);
+ NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e);
@@ -1490,12 +1489,14 @@
private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException {
+ // TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
ifaces);
// fold tethering stats and operations into uid snapshot
final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
+ NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
uidSnapshot.combineAllValues(tetherSnapshot);
final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1505,13 +1506,12 @@
final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
if (vtStats != null) {
vtStats.filter(UID_ALL, ifaces, TAG_ALL);
+ NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
uidSnapshot.combineAllValues(vtStats);
}
uidSnapshot.combineAllValues(mUidOperations);
- // TODO: apply tethering & VC 464xlat adjustments here
-
return uidSnapshot;
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 3814ef3..46f39f2 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -463,10 +463,17 @@
if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
mAbortPostBootUpdate.set(true);
+
+ // Do not reschedule.
+ // TODO: We should reschedule if we didn't process all apps, yet.
+ return false;
} else {
mAbortIdleOptimization.set(true);
+
+ // Reschedule the run.
+ // TODO: Should this be dependent on the stop reason?
+ return true;
}
- return false;
}
private void notifyPinService(ArraySet<String> updatedPackages) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 504184c..73f4274 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -64,6 +64,8 @@
public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
/** Indicates that dexopt should convert to CompactDex. */
public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11;
+ /** Indicates that dexopt should generate an app image */
+ public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index faaa3ba..88caf32 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -267,7 +267,7 @@
final StringBuilder builder = new StringBuilder();
// The current version.
- builder.append("8 ");
+ builder.append("9 ");
builder.append("dexopt");
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index ae48844..f9e11f9 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -60,6 +60,7 @@
import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -454,13 +455,15 @@
pw.increaseIndent();
for (String isa : dexCodeInstructionSets) {
- String status = null;
try {
- status = DexFile.getDexFileStatus(path, isa);
+ String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
+ String compilationStatus = status[0];
+ String compilationReason = status[1];
+ pw.println(isa + ": [status=" + compilationStatus
+ +"] reason=[" + compilationReason + "]");
} catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
+ pw.println(isa + ": [Exception]: " + ioe.getMessage());
}
- pw.println(isa + ": " + status);
}
if (useInfo.isUsedByOtherApps(path)) {
@@ -521,6 +524,10 @@
return getDexFlags(pkg.applicationInfo, compilerFilter, options);
}
+ private boolean isAppImageEnabled() {
+ return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
+ }
+
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
@@ -547,6 +554,14 @@
case PackageManagerService.REASON_INSTALL:
generateCompactDex = false;
}
+ // Use app images only if it is enabled and we are compiling
+ // profile-guided (so the app image doesn't conservatively contain all classes).
+ // If the app didn't request for the splits to be loaded in isolation or if it does not
+ // declare inter-split dependencies, then all the splits will be loaded in the base
+ // apk class loader (in the order of their definition, otherwise disable app images
+ // because they are unsupported for multiple class loaders. b/7269679
+ boolean generateAppImage = isProfileGuidedFilter && (info.splitDependencies == null ||
+ !info.requestsIsolatedSplitLoading()) && isAppImageEnabled();
int dexFlags =
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -554,6 +569,7 @@
| (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
| (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
| (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
+ | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
| hiddenApiFlag;
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7d9736e..92a09d3 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -16,21 +16,21 @@
package android.net.apf;
+import static android.net.util.NetworkConstants.*;
import static android.system.OsConstants.*;
-
import static com.android.internal.util.BitUtils.bytesToBEInt;
import static com.android.internal.util.BitUtils.getUint16;
import static com.android.internal.util.BitUtils.getUint32;
import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.uint16;
import static com.android.internal.util.BitUtils.uint32;
-import static com.android.internal.util.BitUtils.uint8;
-import android.os.SystemClock;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
-import android.net.apf.ApfGenerator;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpClient;
@@ -39,31 +39,29 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
+import android.os.PowerManager;
+import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
-
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.Thread;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
-
import libcore.io.IoBridge;
/**
@@ -215,10 +213,6 @@
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int ICMP6_ROUTER_SOLICITATION = 133;
- private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
- private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
- private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
@@ -258,9 +252,26 @@
private long mUniqueCounter;
@GuardedBy("this")
private boolean mMulticastFilter;
+ @GuardedBy("this")
+ private boolean mInDozeMode;
private final boolean mDrop802_3Frames;
private final int[] mEthTypeBlackList;
+ // Detects doze mode state transitions.
+ private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ final boolean deviceIdle = powerManager.isDeviceIdleMode();
+ setDozeMode(deviceIdle);
+ }
+ }
+ };
+ private final Context mContext;
+
// Our IPv4 address, if we have just one, otherwise null.
@GuardedBy("this")
private byte[] mIPv4Address;
@@ -269,13 +280,14 @@
private int mIPv4PrefixLength;
@VisibleForTesting
- ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
+ ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
IpClient.Callback ipClientCallback, IpConnectivityLog log) {
mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
mInterfaceParams = ifParams;
mMulticastFilter = config.multicastFilter;
mDrop802_3Frames = config.ieee802_3Filter;
+ mContext = context;
// Now fill the black list from the passed array
mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
@@ -284,6 +296,10 @@
// TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
maybeStartFilter();
+
+ // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
+ mContext.registerReceiver(mDeviceIdleReceiver,
+ new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
}
private void log(String s) {
@@ -522,7 +538,7 @@
// to our packet socket. b/29586253
if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
- getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) {
+ getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
throw new InvalidRaException("Not an ICMP6 router advertisement");
}
@@ -889,10 +905,11 @@
private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv6 filter program does:
//
- // if it's not ICMPv6:
- // if it's multicast and we're dropping multicast:
- // drop
- // pass
+ // if we're dropping multicast
+ // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
+ // if it's multicast:
+ // drop
+ // pass
// if it's ICMPv6 RS to any:
// drop
// if it's ICMPv6 NA to ff02::1:
@@ -902,28 +919,44 @@
// Drop multicast if the multicast filter is enabled.
if (mMulticastFilter) {
- // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
- String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
- gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+ final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+ final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
- // Drop all other packets sent to ff00::/8.
+ // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
+ // While awake, let all ICMPv6 multicasts through.
+ if (mInDozeMode) {
+ // Not ICMPv6? -> Proceed to multicast filtering
+ gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
+
+ // ICMPv6 but not ECHO? -> Skip the multicast filter.
+ // (ICMPv6 ECHO requests will go through the multicast filter below).
+ gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+ gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
+ } else {
+ gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
+ }
+
+ // Drop all other packets sent to ff00::/8 (multicast prefix).
+ gen.defineLabel(dropAllIPv6MulticastsLabel);
gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
- // Not multicast and not ICMPv6. Pass.
+ // Not multicast. Pass.
gen.addJump(gen.PASS_LABEL);
- gen.defineLabel(skipIpv6MulticastFilterLabel);
+ gen.defineLabel(skipIPv6MulticastFilterLabel);
} else {
// If not ICMPv6, pass.
gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
}
+ // If we got this far, the packet is ICMPv6. Drop some specific types.
+
// Add unsolicited multicast neighbor announcements filter
String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
// Drop all router solicitations (b/32833400)
- gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL);
// If not neighbor announcements, skip filter.
- gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+ gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
// If to ff02::1, drop.
// TODO: Drop only if they don't contain the address of on-link neighbours.
gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
@@ -1167,9 +1200,9 @@
* Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
* filtering using APF programs.
*/
- public static ApfFilter maybeCreate(ApfConfiguration config,
+ public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
- if (config == null || ifParams == null) return null;
+ if (context == null || config == null || ifParams == null) return null;
ApfCapabilities apfCapabilities = config.apfCapabilities;
if (apfCapabilities == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
@@ -1186,7 +1219,8 @@
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
- return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
+
+ return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
}
public synchronized void shutdown() {
@@ -1196,12 +1230,11 @@
mReceiveThread = null;
}
mRas.clear();
+ mContext.unregisterReceiver(mDeviceIdleReceiver);
}
public synchronized void setMulticastFilter(boolean isEnabled) {
- if (mMulticastFilter == isEnabled) {
- return;
- }
+ if (mMulticastFilter == isEnabled) return;
mMulticastFilter = isEnabled;
if (!isEnabled) {
mNumProgramUpdatesAllowingMulticast++;
@@ -1209,6 +1242,13 @@
installNewProgramLocked();
}
+ @VisibleForTesting
+ public synchronized void setDozeMode(boolean isEnabled) {
+ if (mInDozeMode == isEnabled) return;
+ mInDozeMode = isEnabled;
+ installNewProgramLocked();
+ }
+
/** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
LinkAddress ipv4Address = null;
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index d41fbce..ca8f727 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -367,7 +367,7 @@
*/
public boolean setApfVersion(int version) {
// This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- return version == 2;
+ return version >= 2;
}
private void addInstruction(Instruction instruction) {
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
new file mode 100644
index 0000000..97d20f4
--- /dev/null
+++ b/services/net/java/android/net/dns/ResolvUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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.net.dns;
+
+import android.net.Network;
+import android.net.NetworkUtils;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
+
+import libcore.io.Libcore;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+/**
+ * DNS resolution utility class.
+ *
+ * @hide
+ */
+public class ResolvUtil {
+ // Non-portable DNS resolution flag.
+ private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
+
+ private ResolvUtil() {}
+
+ public static InetAddress[] blockingResolveAllLocally(Network network, String name)
+ throws UnknownHostException {
+ final StructAddrinfo hints = new StructAddrinfo();
+ // Unnecessary, but expressly no AI_ADDRCONFIG.
+ hints.ai_flags = 0;
+ // Fetch all IP addresses at once to minimize re-resolution.
+ hints.ai_family = OsConstants.AF_UNSPEC;
+ hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+ final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
+
+ try {
+ return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
+ } catch (GaiException gai) {
+ gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
+ return null; // keep compiler quiet
+ }
+ }
+
+ public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
+ final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
+ return new Network((int) netidForResolv);
+ }
+}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 9863370..87249df 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -40,6 +40,7 @@
import android.net.util.NetdService;
import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
+import android.os.ConditionVariable;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
@@ -150,6 +151,28 @@
public void setNeighborDiscoveryOffload(boolean enable) {}
}
+ public static class WaitForProvisioningCallback extends Callback {
+ private final ConditionVariable mCV = new ConditionVariable();
+ private LinkProperties mCallbackLinkProperties;
+
+ public LinkProperties waitForProvisioning() {
+ mCV.block();
+ return mCallbackLinkProperties;
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ mCallbackLinkProperties = newLp;
+ mCV.open();
+ }
+
+ @Override
+ public void onProvisioningFailure(LinkProperties newLp) {
+ mCallbackLinkProperties = null;
+ mCV.open();
+ }
+ }
+
// Use a wrapper class to log in order to ensure complete and detailed
// logging. This method is lighter weight than annotations/reflection
// and has the following benefits:
@@ -281,6 +304,11 @@
return this;
}
+ public Builder withoutMultinetworkPolicyTracker() {
+ mConfig.mUsingMultinetworkPolicyTracker = false;
+ return this;
+ }
+
public Builder withoutIpReachabilityMonitor() {
mConfig.mUsingIpReachabilityMonitor = false;
return this;
@@ -343,6 +371,7 @@
/* package */ boolean mEnableIPv4 = true;
/* package */ boolean mEnableIPv6 = true;
+ /* package */ boolean mUsingMultinetworkPolicyTracker = true;
/* package */ boolean mUsingIpReachabilityMonitor = true;
/* package */ int mRequestedPreDhcpActionMs;
/* package */ InitialConfiguration mInitialConfig;
@@ -374,6 +403,7 @@
return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
.add("mEnableIPv4: " + mEnableIPv4)
.add("mEnableIPv6: " + mEnableIPv6)
+ .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
.add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
.add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
.add("mInitialConfig: " + mInitialConfig)
@@ -559,7 +589,6 @@
private final NetlinkTracker mNetlinkTracker;
private final WakeupMessage mProvisioningTimeoutAlarm;
private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private final SharedLog mLog;
private final LocalLog mConnectivityPacketLog;
private final MessageHandlingLogger mMsgStateLogger;
@@ -573,6 +602,7 @@
*/
private LinkProperties mLinkProperties;
private ProvisioningConfiguration mConfiguration;
+ private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
private DhcpResults mDhcpResults;
@@ -685,9 +715,6 @@
mLinkProperties = new LinkProperties();
mLinkProperties.setInterfaceName(mInterfaceName);
- mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
- () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
-
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
@@ -719,8 +746,6 @@
} catch (RemoteException e) {
logError("Couldn't register NetlinkTracker: %s", e);
}
-
- mMultinetworkPolicyTracker.start();
}
private void stopStateMachineUpdaters() {
@@ -729,8 +754,6 @@
} catch (RemoteException e) {
logError("Couldn't unregister NetlinkTracker: %s", e);
}
-
- mMultinetworkPolicyTracker.shutdown();
}
@Override
@@ -1028,7 +1051,8 @@
// Note that we can still be disconnected by IpReachabilityMonitor
// if the IPv6 default gateway (but not the IPv6 DNS servers; see
// accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
+ final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
+ && !mMultinetworkPolicyTracker.getAvoidBadWifi();
// Additionally:
//
@@ -1490,7 +1514,7 @@
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
apfConfig.ethTypeBlackList =
mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
- mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
+ mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
@@ -1520,6 +1544,13 @@
return;
}
+ if (mConfiguration.mUsingMultinetworkPolicyTracker) {
+ mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
+ mContext, getHandler(),
+ () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
+ mMultinetworkPolicyTracker.start();
+ }
+
if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
doImmediateProvisioningFailure(
IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
@@ -1537,6 +1568,11 @@
mIpReachabilityMonitor = null;
}
+ if (mMultinetworkPolicyTracker != null) {
+ mMultinetworkPolicyTracker.shutdown();
+ mMultinetworkPolicyTracker = null;
+ }
+
if (mDhcpClient != null) {
mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpClient.doQuit();
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 508a43d..2eb36a2 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -114,35 +114,6 @@
public static class Callback extends IpClient.Callback {
}
- public static class WaitForProvisioningCallback extends Callback {
- private LinkProperties mCallbackLinkProperties;
-
- public LinkProperties waitForProvisioning() {
- synchronized (this) {
- try {
- wait();
- } catch (InterruptedException e) {}
- return mCallbackLinkProperties;
- }
- }
-
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- synchronized (this) {
- mCallbackLinkProperties = newLp;
- notify();
- }
- }
-
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- synchronized (this) {
- mCallbackLinkProperties = null;
- notify();
- }
- }
- }
-
public IpManager(Context context, String ifName, Callback callback) {
super(context, ifName, callback);
}
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java
new file mode 100644
index 0000000..9f26fa1
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceSet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.net.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+/**
+ * @hide
+ */
+public class InterfaceSet {
+ public final Set<String> ifnames;
+
+ public InterfaceSet(String... names) {
+ final Set<String> nameSet = new HashSet<>();
+ for (String name : names) {
+ if (name != null) nameSet.add(name);
+ }
+ ifnames = Collections.unmodifiableSet(nameSet);
+ }
+
+ @Override
+ public String toString() {
+ final StringJoiner sj = new StringJoiner(",", "[", "]");
+ for (String ifname : ifnames) sj.add(ifname);
+ return sj.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null
+ && obj instanceof InterfaceSet
+ && ifnames.equals(((InterfaceSet)obj).ifnames);
+ }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 984c9f8..53fd01f 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -136,6 +136,8 @@
* - https://tools.ietf.org/html/rfc4861
*/
public static final int ICMPV6_HEADER_MIN_LEN = 4;
+ public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+ public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
public static final int ICMPV6_ROUTER_SOLICITATION = 133;
public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134;
public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
@@ -147,7 +149,6 @@
public static final int ICMPV6_ND_OPTION_TLLA = 2;
public static final int ICMPV6_ND_OPTION_MTU = 5;
- public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
/**
* UDP constants.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9a53d8c..ca444d4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2598,7 +2598,6 @@
}
/**
- *
* Request audio routing to a specific bluetooth device. Calling this method may result in
* the device routing audio to a different bluetooth device than the one specified if the
* bluetooth stack is unable to route audio to the requested device.
@@ -2609,13 +2608,13 @@
* Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
* self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
* <p>
- * See also {@link InCallService#requestBluetoothAudio(String)}
- * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
- * {@link BluetoothDevice#getAddress()}.
+ * See also {@link InCallService#requestBluetoothAudio(BluetoothDevice)}
+ * @param bluetoothDevice The bluetooth device to connect to.
*/
- public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ public void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
for (Listener l : mListeners) {
- l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
+ l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH,
+ bluetoothDevice.getAddress());
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index af65c65..bd25ab2 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -428,12 +428,11 @@
* A list of available devices can be obtained via
* {@link CallAudioState#getSupportedBluetoothDevices()}
*
- * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
- * {@link BluetoothDevice#getAddress()}.
+ * @param bluetoothDevice The bluetooth device to connect to.
*/
- public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ public final void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
if (mPhone != null) {
- mPhone.requestBluetoothAudio(bluetoothAddress);
+ mPhone.requestBluetoothAudio(bluetoothDevice.getAddress());
}
}
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 08f8bb6..890a6ea 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* CellIdentity represents the identity of a unique cell. This is the base class for
@@ -84,8 +87,16 @@
/** @hide */
protected final String mMncStr;
+ // long alpha Operator Name String or Enhanced Operator Name String
/** @hide */
- protected CellIdentity(String tag, int type, String mcc, String mnc) {
+ protected final String mAlphaLong;
+ // short alpha Operator Name String or Enhanced Operator Name String
+ /** @hide */
+ protected final String mAlphaShort;
+
+ /** @hide */
+ protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+ String alphas) {
mTag = tag;
mType = type;
@@ -113,6 +124,8 @@
mMncStr = null;
log("invalid MNC format: " + mnc);
}
+ mAlphaLong = alphal;
+ mAlphaShort = alphas;
}
/** Implement the Parcelable interface */
@@ -138,6 +151,40 @@
}
/**
+ * @return The long alpha tag associated with the current scan result (may be the operator
+ * name string or extended operator name string). May be null if unknown.
+ */
+ @Nullable
+ public CharSequence getOperatorAlphaLong() {
+ return mAlphaLong;
+ }
+
+ /**
+ * @return The short alpha tag associated with the current scan result (may be the operator
+ * name string or extended operator name string). May be null if unknown.
+ */
+ @Nullable
+ public CharSequence getOperatorAlphaShort() {
+ return mAlphaShort;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CellIdentity)) {
+ return false;
+ }
+
+ CellIdentity o = (CellIdentity) other;
+ return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+ }
+
+ /**
* Used by child classes for parceling.
*
* @hide
@@ -147,6 +194,8 @@
dest.writeInt(type);
dest.writeString(mMccStr);
dest.writeString(mMncStr);
+ dest.writeString(mAlphaLong);
+ dest.writeString(mAlphaShort);
}
/**
@@ -154,7 +203,8 @@
* @hide
*/
protected CellIdentity(String tag, int type, Parcel source) {
- this(tag, type, source.readString(), source.readString());
+ this(tag, type, source.readString(), source.readString(),
+ source.readString(), source.readString());
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 713ac00..9a18c30 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,9 +16,7 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
-import android.text.TextUtils;
import java.util.Objects;
@@ -49,23 +47,17 @@
* to +90 degrees).
*/
private final int mLatitude;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityCdma() {
- super(TAG, TYPE_CDMA, null, null);
+ super(TAG, TYPE_CDMA, null, null, null, null);
mNetworkId = Integer.MAX_VALUE;
mSystemId = Integer.MAX_VALUE;
mBasestationId = Integer.MAX_VALUE;
mLongitude = Integer.MAX_VALUE;
mLatitude = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -100,7 +92,7 @@
*/
public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
String alphas) {
- super(TAG, TYPE_CDMA, null, null);
+ super(TAG, TYPE_CDMA, null, null, alphal, alphas);
mNetworkId = nid;
mSystemId = sid;
mBasestationId = bid;
@@ -110,8 +102,6 @@
} else {
mLongitude = mLatitude = Integer.MAX_VALUE;
}
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityCdma(CellIdentityCdma cid) {
@@ -178,28 +168,10 @@
return mLatitude;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
- mAlphaLong, mAlphaShort);
+ super.hashCode());
}
@Override
@@ -219,8 +191,7 @@
&& mBasestationId == o.mBasestationId
&& mLatitude == o.mLatitude
&& mLongitude == o.mLongitude
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -246,8 +217,6 @@
dest.writeInt(mBasestationId);
dest.writeInt(mLongitude);
dest.writeInt(mLatitude);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -258,8 +227,6 @@
mBasestationId = in.readInt();
mLongitude = in.readInt();
mLatitude = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index aae7929..b167850 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -37,22 +36,16 @@
private final int mArfcn;
// 6-bit Base Station Identity Code
private final int mBsic;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityGsm() {
- super(TAG, TYPE_GSM, null, null);
+ super(TAG, TYPE_GSM, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mArfcn = Integer.MAX_VALUE;
mBsic = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
* public constructor
@@ -98,16 +91,13 @@
*/
public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_GSM, mccStr, mncStr);
+ super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
mLac = lac;
mCid = cid;
mArfcn = arfcn;
// In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
// for inbound parcels
mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityGsm(CellIdentityGsm cid) {
@@ -188,24 +178,6 @@
return mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
/** @hide */
@Override
public int getChannelNumber() {
@@ -223,7 +195,7 @@
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, super.hashCode());
}
@Override
@@ -243,8 +215,7 @@
&& mBsic == o.mBsic
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -270,8 +241,6 @@
dest.writeInt(mCid);
dest.writeInt(mArfcn);
dest.writeInt(mBsic);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -281,8 +250,6 @@
mCid = in.readInt();
mArfcn = in.readInt();
mBsic = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 9b3ef56..d421cbd 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -37,10 +36,6 @@
private final int mTac;
// 18-bit Absolute RF Channel Number
private final int mEarfcn;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
// cell bandwidth, in kHz
private final int mBandwidth;
@@ -48,14 +43,12 @@
* @hide
*/
public CellIdentityLte() {
- super(TAG, TYPE_LTE, null, null);
+ super(TAG, TYPE_LTE, null, null, null, null);
mCi = Integer.MAX_VALUE;
mPci = Integer.MAX_VALUE;
mTac = Integer.MAX_VALUE;
mEarfcn = Integer.MAX_VALUE;
mBandwidth = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -105,14 +98,12 @@
*/
public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_LTE, mccStr, mncStr);
+ super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
mCi = ci;
mPci = pci;
mTac = tac;
mEarfcn = earfcn;
mBandwidth = bandwidth;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityLte(CellIdentityLte cid) {
@@ -198,24 +189,6 @@
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
/** @hide */
@Override
public int getChannelNumber() {
@@ -224,7 +197,7 @@
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+ return Objects.hash(mCi, mPci, mTac, super.hashCode());
}
@Override
@@ -245,8 +218,7 @@
&& mBandwidth == o.mBandwidth
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -274,8 +246,6 @@
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
dest.writeInt(mBandwidth);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -286,8 +256,6 @@
mTac = in.readInt();
mEarfcn = in.readInt();
mBandwidth = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 7475c74..3070bd1 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -35,21 +34,15 @@
private final int mCid;
// 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
private final int mCpid;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityTdscdma() {
- super(TAG, TYPE_TDSCDMA, null, null);
+ super(TAG, TYPE_TDSCDMA, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mCpid = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -76,12 +69,10 @@
* @hide
*/
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
- super(TAG, TYPE_TDSCDMA, mcc, mnc);
+ super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
mLac = lac;
mCid = cid;
mCpid = cpid;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
@@ -97,12 +88,10 @@
*/
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
String alphal, String alphas) {
- super(TAG, TYPE_TDSCDMA, mcc, mnc);
+ super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
mLac = lac;
mCid = cid;
mCpid = cpid;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityTdscdma(CellIdentityTdscdma cid) {
@@ -151,31 +140,9 @@
return mCpid;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- *
- * @hide
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- *
- * @hide
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, mCpid, super.hashCode());
}
@Override
@@ -194,8 +161,7 @@
&& mLac == o.mLac
&& mCid == o.mCid
&& mCpid == o.mCpid
- && mAlphaLong == o.mAlphaLong
- && mAlphaShort == o.mAlphaShort;
+ && super.equals(other);
}
@Override
@@ -219,8 +185,6 @@
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mCpid);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -229,8 +193,6 @@
mLac = in.readInt();
mCid = in.readInt();
mCpid = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 52fa54f..f1167a6 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -37,22 +36,16 @@
private final int mPsc;
// 16-bit UMTS Absolute RF Channel Number
private final int mUarfcn;
- // long alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaLong;
- // short alpha Operator Name String or Enhanced Operator Name String
- private final String mAlphaShort;
/**
* @hide
*/
public CellIdentityWcdma() {
- super(TAG, TYPE_TDSCDMA, null, null);
+ super(TAG, TYPE_TDSCDMA, null, null, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mPsc = Integer.MAX_VALUE;
mUarfcn = Integer.MAX_VALUE;
- mAlphaLong = null;
- mAlphaShort = null;
}
/**
* public constructor
@@ -99,13 +92,11 @@
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
String mccStr, String mncStr, String alphal, String alphas) {
- super(TAG, TYPE_WCDMA, mccStr, mncStr);
+ super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
mLac = lac;
mCid = cid;
mPsc = psc;
mUarfcn = uarfcn;
- mAlphaLong = alphal;
- mAlphaShort = alphas;
}
private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -179,27 +170,9 @@
return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
}
- /**
- * @return The long alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaLong() {
- return mAlphaLong;
- }
-
- /**
- * @return The short alpha tag associated with the current scan result (may be the operator
- * name string or extended operator name string). May be null if unknown.
- */
- @Nullable
- public CharSequence getOperatorAlphaShort() {
- return mAlphaShort;
- }
-
@Override
public int hashCode() {
- return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+ return Objects.hash(mLac, mCid, mPsc, super.hashCode());
}
/**
@@ -232,8 +205,7 @@
&& mUarfcn == o.mUarfcn
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
- && TextUtils.equals(mAlphaLong, o.mAlphaLong)
- && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ && super.equals(other);
}
@Override
@@ -259,8 +231,6 @@
dest.writeInt(mCid);
dest.writeInt(mPsc);
dest.writeInt(mUarfcn);
- dest.writeString(mAlphaLong);
- dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
@@ -270,8 +240,6 @@
mCid = in.readInt();
mPsc = in.readInt();
mUarfcn = in.readInt();
- mAlphaLong = in.readString();
- mAlphaShort = in.readString();
if (DBG) log(toString());
}
@@ -295,4 +263,4 @@
protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
return new CellIdentityWcdma(in);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 81a966b..d9fdd97 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,13 +33,15 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
-import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStatusListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.InternalDownloadProgressListener;
import android.telephony.mbms.InternalDownloadSessionCallback;
-import android.telephony.mbms.InternalDownloadStateCallback;
-import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsTempFileProvider;
import android.telephony.mbms.MbmsUtils;
@@ -56,8 +60,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
/**
* This class provides functionality for file download over MBMS.
*/
@@ -240,8 +242,10 @@
private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
private final InternalDownloadSessionCallback mInternalCallback;
- private final Map<DownloadStateCallback, InternalDownloadStateCallback>
- mInternalDownloadCallbacks = new HashMap<>();
+ private final Map<DownloadStatusListener, InternalDownloadStatusListener>
+ mInternalDownloadStatusListeners = new HashMap<>();
+ private final Map<DownloadProgressListener, InternalDownloadProgressListener>
+ mInternalDownloadProgressListeners = new HashMap<>();
private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
MbmsDownloadSessionCallback callback) {
@@ -333,6 +337,12 @@
sIsInitialized.set(false);
return;
}
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an"
+ + " unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false);
@@ -384,6 +394,11 @@
}
try {
int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+ if (returnCode == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null);
}
@@ -439,6 +454,11 @@
try {
int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, null);
return;
@@ -510,11 +530,13 @@
*
* Asynchronous errors through the callback may include any error not specific to the
* streaming use-case.
+ *
+ * If no error is delivered via the callback after calling this method, that means that the
+ * middleware has successfully started the download or scheduled the download, if the download
+ * is at a future time.
* @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 int download(@NonNull DownloadRequest request) {
+ public void download(@NonNull DownloadRequest request) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -536,12 +558,19 @@
int result = downloadService.download(request);
if (result == MbmsErrors.SUCCESS) {
writeDownloadRequestToken(request);
+ } else {
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown"
+ + " error code");
+ }
+ sendErrorToApp(result, null);
}
- return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
}
}
@@ -569,111 +598,232 @@
}
/**
- * Registers a callback for a {@link DownloadRequest} previously requested via
+ * Registers a listener download status for a {@link DownloadRequest} previously requested via
* {@link #download(DownloadRequest)}. This callback will only be called as long as both this
* app and the middleware are both running -- if either one stops, no further calls on the
- * provided {@link DownloadStateCallback} will be enqueued.
+ * provided {@link DownloadStatusListener} will be enqueued.
*
* If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}.
*
+ * If the operation encountered an error, the error code will be delivered via
+ * {@link MbmsDownloadSessionCallback#onError}.
+ *
* @param request The {@link DownloadRequest} that you want updates on.
- * @param executor The {@link Executor} on which calls to {@code callback} should be executed.
- * @param callback The callback that should be called when the middleware has information to
- * share on the download.
- * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
- * and some other error code otherwise.
+ * @param executor The {@link Executor} on which calls to {@code listener } should be executed.
+ * @param listener The listener that should be called when the middleware has information to
+ * share on the status download.
*/
- public int registerStateCallback(@NonNull DownloadRequest request,
- @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
+ public void addStatusListener(@NonNull DownloadRequest request,
+ @NonNull Executor executor, @NonNull DownloadStatusListener listener) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
- InternalDownloadStateCallback internalCallback =
- new InternalDownloadStateCallback(callback, executor);
+ InternalDownloadStatusListener internalListener =
+ new InternalDownloadStatusListener(listener, executor);
try {
- int result = downloadService.registerStateCallback(request, internalCallback,
- callback.getCallbackFilterFlags());
+ int result = downloadService.addStatusListener(request, internalListener);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- return result;
+ sendErrorToApp(result, null);
+ return;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
}
- mInternalDownloadCallbacks.put(callback, internalCallback);
- return MbmsErrors.SUCCESS;
+ mInternalDownloadStatusListeners.put(listener, internalListener);
}
/**
- * Un-register a callback previously registered via
- * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}. After
- * this method is called, no further callbacks will be enqueued on the {@link Handler}
+ * Un-register a listener previously registered via
+ * {@link #addStatusListener(DownloadRequest, Executor, DownloadStatusListener)}. After
+ * this method is called, no further calls will be enqueued on the {@link Executor}
* provided upon registration, even if this method throws an exception.
*
* If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}.
*
+ * If the operation encountered an error, the error code will be delivered via
+ * {@link MbmsDownloadSessionCallback#onError}.
+ *
* @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.
+ * @param listener The listener provided during registration.
*/
- public int unregisterStateCallback(@NonNull DownloadRequest request,
- @NonNull DownloadStateCallback callback) {
+ public void removeStatusListener(@NonNull DownloadRequest request,
+ @NonNull DownloadStatusListener listener) {
try {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
- InternalDownloadStateCallback internalCallback =
- mInternalDownloadCallbacks.get(callback);
- if (internalCallback == null) {
- throw new IllegalArgumentException("Provided callback was never registered");
+ InternalDownloadStatusListener internalListener =
+ mInternalDownloadStatusListeners.get(listener);
+ if (internalListener == null) {
+ throw new IllegalArgumentException("Provided listener was never registered");
}
try {
- int result = downloadService.unregisterStateCallback(request, internalCallback);
+ int result = downloadService.removeStatusListener(request, internalListener);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an"
+ + " unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- return result;
+ sendErrorToApp(result, null);
+ return;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
}
} finally {
- InternalDownloadStateCallback internalCallback =
- mInternalDownloadCallbacks.remove(callback);
+ InternalDownloadStatusListener internalCallback =
+ mInternalDownloadStatusListeners.remove(listener);
if (internalCallback != null) {
internalCallback.stop();
}
}
- return MbmsErrors.SUCCESS;
+ }
+
+ /**
+ * Registers a listener for progress for a {@link DownloadRequest} previously requested via
+ * {@link #download(DownloadRequest)}. This listener will only be called as long as both this
+ * app and the middleware are both running -- if either one stops, no further calls on the
+ * provided {@link DownloadProgressListener} will be enqueued.
+ *
+ * If the middleware is not aware of the specified download request,
+ * this method will throw an {@link IllegalArgumentException}.
+ *
+ * If the operation encountered an error, the error code will be delivered via
+ * {@link MbmsDownloadSessionCallback#onError}.
+ *
+ * @param request The {@link DownloadRequest} that you want updates on.
+ * @param executor The {@link Executor} on which calls to {@code listener} should be executed.
+ * @param listener The listener that should be called when the middleware has information to
+ * share on the progress of the download.
+ */
+ public void addProgressListener(@NonNull DownloadRequest request,
+ @NonNull Executor executor, @NonNull DownloadProgressListener listener) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalDownloadProgressListener internalListener =
+ new InternalDownloadProgressListener(listener, executor);
+
+ try {
+ int result = downloadService.addProgressListener(request, internalListener);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sIsInitialized.set(false);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
+ }
+ mInternalDownloadProgressListeners.put(listener, internalListener);
+ }
+
+ /**
+ * Un-register a listener previously registered via
+ * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}. After
+ * this method is called, no further callbacks will be enqueued on the {@link Handler}
+ * provided upon registration, even if this method throws an exception.
+ *
+ * If the middleware is not aware of the specified download request,
+ * this method will throw an {@link IllegalArgumentException}.
+ *
+ * If the operation encountered an error, the error code will be delivered via
+ * {@link MbmsDownloadSessionCallback#onError}.
+ *
+ * @param request The {@link DownloadRequest} provided during registration
+ * @param listener The listener provided during registration.
+ */
+ public void removeProgressListener(@NonNull DownloadRequest request,
+ @NonNull DownloadProgressListener listener) {
+ try {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalDownloadProgressListener internalListener =
+ mInternalDownloadProgressListeners.get(listener);
+ if (internalListener == null) {
+ throw new IllegalArgumentException("Provided listener was never registered");
+ }
+
+ try {
+ int result = downloadService.removeProgressListener(request, internalListener);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not"
+ + " return an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sIsInitialized.set(false);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
+ }
+ } finally {
+ InternalDownloadProgressListener internalCallback =
+ mInternalDownloadProgressListeners.remove(listener);
+ if (internalCallback != null) {
+ internalCallback.stop();
+ }
+ }
}
/**
* Attempts to cancel the specified {@link DownloadRequest}.
*
- * If the middleware is not aware of the specified download request,
- * this method will throw an {@link IllegalArgumentException}.
+ * If the operation encountered an error, the error code will be delivered via
+ * {@link MbmsDownloadSessionCallback#onError}.
*
* @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 int cancelDownload(@NonNull DownloadRequest downloadRequest) {
+ public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -681,18 +831,20 @@
try {
int result = downloadService.cancelDownload(downloadRequest);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
- if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
- throw new IllegalArgumentException("Unknown download request.");
- }
+ sendErrorToApp(result, null);
} else {
deleteDownloadRequestToken(downloadRequest);
}
- return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
}
}
@@ -700,9 +852,9 @@
* Requests information about the state of a file pending download.
*
* The state will be delivered as a callback via
- * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
+ * {@link DownloadStatusListener#onStatusUpdated(DownloadRequest, FileInfo, int)}. If no such
* callback has been registered via
- * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}, this
+ * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}, this
* method will be a no-op.
*
* If the middleware has no record of the
@@ -720,6 +872,11 @@
try {
int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
@@ -764,6 +921,11 @@
try {
int result = downloadService.resetDownloadKnowledge(downloadRequest);
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
@@ -822,11 +984,11 @@
try {
if (!token.createNewFile()) {
throw new RuntimeException("Failed to create download token for request "
- + request);
+ + request + ". Token location is " + token.getPath());
}
} catch (IOException e) {
throw new RuntimeException("Failed to create download token for request " + request
- + " due to IOException " + e);
+ + " due to IOException " + e + ". Attempted to write to " + token.getPath());
}
}
@@ -880,10 +1042,6 @@
}
private void sendErrorToApp(int errorCode, String message) {
- try {
- mInternalCallback.onError(errorCode, message);
- } catch (RemoteException e) {
- // Ignore, should not happen locally.
- }
+ mInternalCallback.onError(errorCode, message);
}
}
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index 42c760d4..cd465d2 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -26,8 +28,8 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.MbmsUtils;
@@ -44,8 +46,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
/**
* This class provides functionality for streaming media over MBMS.
*/
@@ -208,6 +208,11 @@
try {
int returnCode = streamingService.requestUpdateStreamingServices(
mSubscriptionId, serviceClassList);
+ if (returnCode == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null);
}
@@ -255,6 +260,11 @@
try {
int returnCode = streamingService.startStreaming(
mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+ if (returnCode == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null);
return null;
@@ -301,6 +311,12 @@
sIsInitialized.set(false);
return;
}
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return"
+ + " an unknown error code");
+ }
if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false);
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 92ebf36..dbad02f 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -83,6 +83,8 @@
} catch (RemoteException e) {
Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
}
+ } else {
+ Rlog.e(mTag, "Weak reference of callback is null.");
}
}
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2516d51..46e2adb 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -97,7 +97,7 @@
/** This cause is used to report a resource unavailable event only when no other
* cause in the resource unavailable class applies
*/
- public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 44;
+ public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47;
/** The requested quality of service (ITU-T X.213) cannot be provided */
public static final int QOS_NOT_AVAIL = 49;
/** The facility could not be provided by the network because the user has no
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index ea74bac..f336af1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -198,7 +198,10 @@
*/
public static SmsMessage createFromPdu(byte[] pdu, String format) {
SmsMessageBase wrappedMessage;
-
+ if (pdu == null) {
+ Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+ return null;
+ }
if (SmsConstants.FORMAT_3GPP2.equals(format)) {
wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
} else if (SmsConstants.FORMAT_3GPP.equals(format)) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 77413d9c..936505c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -335,7 +335,7 @@
return this.mCountryIso;
}
- /** @return whether the subscription is an embedded one. */
+ /** @return whether the subscription is an eUICC one. */
public boolean isEmbedded() {
return this.mIsEmbedded;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 05b8b57..1d7b724 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1704,26 +1704,28 @@
*
* @param info The subscription to check.
* @return whether the app is authorized to manage this subscription per its metadata.
- * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @throws IllegalArgumentException if this subscription is not embedded.
*/
public boolean canManageSubscription(SubscriptionInfo info) {
return canManageSubscription(info, mContext.getPackageName());
}
/**
- * Checks whether the given app is authorized to manage the given subscription according to its
- * metadata. Only supported for embedded subscriptions (if {@code SubscriptionInfo#isEmbedded}
+ * Checks whether the given app is authorized to manage the given subscription. An app can only
+ * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the
+ * {@link android.telephony.SubscriptionInfo} with the access status.
+ * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded}
* returns true).
*
* @param info The subscription to check.
* @param packageName Package name of the app to check.
- * @return whether the app is authorized to manage this subscription per its metadata.
- * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @return whether the app is authorized to manage this subscription per its access rules.
+ * @throws IllegalArgumentException if this subscription is not embedded.
* @hide
*/
public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
if (!info.isEmbedded()) {
- throw new UnsupportedOperationException("Not an embedded subscription");
+ throw new IllegalArgumentException("Not an embedded subscription");
}
if (info.getAccessRules() == null) {
return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fc003a6..f55061a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -27,6 +27,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.app.PendingIntent;
@@ -40,6 +41,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -214,6 +216,10 @@
return ActivityThread.currentOpPackageName();
}
+ private boolean isSystemProcess() {
+ return Process.myUid() == Process.SYSTEM_UID;
+ }
+
/**
* Returns the multi SIM variant
* Returns DSDS for Dual SIM Dual Standby
@@ -1030,6 +1036,13 @@
public static final int UNKNOWN_CARRIER_ID = -1;
/**
+ * An unknown carrier id list version.
+ * @hide
+ */
+ @TestApi
+ public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1;
+
+ /**
* Broadcast Action: The subscription carrier identity has changed.
* This intent could be sent on the following events:
* <ul>
@@ -2785,18 +2798,17 @@
* @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the
* IMSI and IMPI. This includes the public key and the key identifier. This information
* will be stored in the device keystore. The system will return a null when no key was
- * found, and the carrier does not require a key. The system will throw the following
- * exceptions:
- * 1. IllegalArgumentException when an invalid key is sent.
- * 2. RuntimeException if the key is required but not found; and also if there was an
- * internal exception.
+ * found, and the carrier does not require a key. The system will throw
+ * IllegalArgumentException when an invalid key is sent or when key is required but
+ * not found.
* @hide
*/
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
try {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null) {
- throw new RuntimeException("IMSI error: Subscriber Info is null");
+ Rlog.e(TAG,"IMSI error: Subscriber Info is null");
+ return null;
}
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) {
@@ -2804,19 +2816,46 @@
}
ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
subId, keyType, mContext.getOpPackageName());
- if (imsiEncryptionInfo == null
- && isImsiEncryptionRequired(subId, keyType)) {
+ if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) {
Rlog.e(TAG, "IMSI error: key is required but not found");
- throw new RuntimeException("IMSI error: key is required but not found");
+ throw new IllegalArgumentException("IMSI error: key is required but not found");
}
return imsiEncryptionInfo;
} catch (RemoteException ex) {
Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
- throw new RuntimeException("IMSI error: Remote Exception");
} catch (NullPointerException ex) {
// This could happen before phone restarts due to crashing
Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
- throw new RuntimeException("IMSI error: Null Pointer exception");
+ }
+ return null;
+ }
+
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * @hide
+ */
+ public void resetCarrierKeysForImsiEncryption() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null) {
+ Rlog.e(TAG, "IMSI error: Subscriber Info is null");
+ if (!isSystemProcess()) {
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
+ }
+ return;
+ }
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
@@ -2855,7 +2894,7 @@
* device keystore.
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* @param imsiEncryptionInfo which includes the Key Type, the Public Key
* (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
* The keyIdentifier Attribute value pair that helps a server locate
@@ -2894,7 +2933,7 @@
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getGroupIdLevel1(mContext.getOpPackageName());
+ return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -3801,19 +3840,22 @@
*
* @throws SecurityException if the caller does not have carrier privileges or is not the
* current default dialer
- *
- * @throws IllegalStateException if telephony service is unavailable.
*/
public void sendDialerSpecialCode(String inputCode) {
try {
final ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ if (!isSystemProcess()) {
+ throw new RuntimeException("Telephony service unavailable");
+ }
+ return;
+ }
telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
} catch (RemoteException ex) {
// This could happen if binder process crashes.
- ex.rethrowFromSystemServer();
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- throw new IllegalStateException("Telephony service unavailable");
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
@@ -4965,10 +5007,10 @@
String v = android.provider.Settings.Global.getString(cr, name);
if (index == Integer.MAX_VALUE) {
- throw new RuntimeException("putIntAtIndex index == MAX_VALUE index=" + index);
+ throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index);
}
if (index < 0) {
- throw new RuntimeException("putIntAtIndex index < 0 index=" + index);
+ throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index);
}
if (v != null) {
valArray = v.split(",");
@@ -6307,34 +6349,39 @@
*
* @param enable Whether to enable mobile data.
*
- * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
*/
- @Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
- setUserMobileDataEnabled(enable);
+ setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}
/**
* @hide
- * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+ * @deprecated use {@link #setDataEnabled(boolean)} instead.
*/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(int subId, boolean enable) {
- setUserMobileDataEnabled(subId, enable);
+ try {
+ Log.d(TAG, "setDataEnabled: enabled=" + enable);
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setUserDataEnabled(subId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+ }
}
/**
- * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+ * @deprecated use {@link #isDataEnabled()} instead.
* @hide
*/
@SystemApi
@Deprecated
public boolean getDataEnabled() {
- return isUserMobileDataEnabled();
+ return isDataEnabled();
}
/**
@@ -6354,22 +6401,28 @@
* {@link ConnectivityManager#getRestrictBackgroundStatus}.
*
* @return true if mobile data is enabled.
- *
- * @deprecated use {@link #isUserMobileDataEnabled()} instead.
*/
- @Deprecated
public boolean isDataEnabled() {
- return isUserMobileDataEnabled();
+ return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
/**
- * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+ * @deprecated use {@link #isDataEnabled()} instead.
* @hide
*/
@Deprecated
@SystemApi
public boolean getDataEnabled(int subId) {
- return isUserMobileDataEnabled(subId);
+ boolean retVal = false;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.isUserDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
}
/** @hide */
@@ -7225,7 +7278,6 @@
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
- ex.rethrowAsRuntimeException();
}
return UNKNOWN_CARRIER_ID;
}
@@ -7250,7 +7302,6 @@
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
- ex.rethrowAsRuntimeException();
}
return null;
}
@@ -7600,56 +7651,12 @@
}
/**
- * Turns mobile data on or off.
- * If the {@link TelephonyManager} object has been created with
- * {@link #createForSubscriptionId}, this API applies to the given subId.
- * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
- * app has carrier privileges (see {@link #hasCarrierPrivileges}.
- *
- * @param enable Whether to enable mobile data.
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setUserMobileDataEnabled(boolean enable) {
- setUserMobileDataEnabled(
- getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
- }
-
- /**
- * Returns whether mobile data is enabled or not per user setting. There are other factors
- * that could disable mobile data, but they are not considered here.
- *
- * If this object has been created with {@link #createForSubscriptionId}, applies to the given
- * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
- *
- * <p>Requires one of the following permissions:
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
- * calling app has carrier privileges (see {@link #hasCarrierPrivileges}.
- *
- * <p>Note that this does not take into account any data restrictions that may be present on the
- * calling app. Such restrictions may be inspected with
- * {@link ConnectivityManager#getRestrictBackgroundStatus}.
- *
- * @return true if mobile data is enabled.
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.ACCESS_NETWORK_STATE,
- android.Manifest.permission.MODIFY_PHONE_STATE
- })
- public boolean isUserMobileDataEnabled() {
- return isUserMobileDataEnabled(
- getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
- }
-
- /**
* @hide
- * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
- * policyDataEnabled etc to give a final decision.
+ * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+ * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+ * capable of using.
*/
- public boolean isMobileDataEnabled() {
+ public boolean isDataCapable() {
boolean retVal = false;
try {
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7664,35 +7671,6 @@
}
/**
- * Utility class of {@link #isUserMobileDataEnabled()};
- */
- private boolean isUserMobileDataEnabled(int subId) {
- boolean retVal = false;
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- retVal = telephony.isUserDataEnabled(subId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
- } catch (NullPointerException e) {
- }
- return retVal;
- }
-
- /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- private void setUserMobileDataEnabled(int subId, boolean enable) {
- try {
- Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.setUserDataEnabled(subId, enable);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
- }
- }
-
- /**
* In this mode, modem will not send specified indications when screen is off.
* @hide
*/
@@ -7730,11 +7708,25 @@
*/
public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED = 0x4;
+ /**
+ * The indication for link capacity estimate update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE = 0x8;
+
+ /**
+ * The indication for physical channel config update.
+ * @hide
+ */
+ public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10;
+
/** @hide */
@IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
INDICATION_FILTER_SIGNAL_STRENGTH,
INDICATION_FILTER_FULL_NETWORK_STATE,
- INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+ INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+ INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+ INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationFilters{}
@@ -7767,7 +7759,54 @@
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
- ex.rethrowAsRuntimeException();
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
}
}
+
+ /**
+ * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+ * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
+ * (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ */
+ @TestApi
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String plmn, String spn) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setCarrierTestOverride(
+ getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ }
+
+ /**
+ * A test API to return installed carrier id list version
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @hide
+ */
+ @TestApi
+ public int getCarrierIdListVersion() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCarrierIdListVersion(getSubId());
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID_LIST_VERSION;
+ }
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.StringDef;
import android.content.ContentValues;
import android.database.Cursor;
import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
@@ -28,17 +28,15 @@
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -46,25 +44,184 @@
*/
public class ApnSetting implements Parcelable {
- static final String LOG_TAG = "ApnSetting";
+ private static final String LOG_TAG = "ApnSetting";
private static final boolean VDBG = false;
+ private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+ private static final Map<Integer, String> APN_TYPE_INT_MAP;
+ private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+ private static final Map<Integer, String> PROTOCOL_INT_MAP;
+ private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+ private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+ private static final int NOT_IN_MAP_INT = -1;
+ private static final int NO_PORT_SPECIFIED = -1;
+
+ /** All APN types except IA. */
+ private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+ /** APN type for default data traffic and HiPri traffic. */
+ public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+ /** APN type for MMS traffic. */
+ public static final int TYPE_MMS = ApnTypes.MMS;
+ /** APN type for SUPL assisted GPS. */
+ public static final int TYPE_SUPL = ApnTypes.SUPL;
+ /** APN type for DUN traffic. */
+ public static final int TYPE_DUN = ApnTypes.DUN;
+ /** APN type for HiPri traffic. */
+ public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+ /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+ public static final int TYPE_FOTA = ApnTypes.FOTA;
+ /** APN type for IMS. */
+ public static final int TYPE_IMS = ApnTypes.IMS;
+ /** APN type for CBS. */
+ public static final int TYPE_CBS = ApnTypes.CBS;
+ /** APN type for IA Initial Attach APN. */
+ public static final int TYPE_IA = ApnTypes.IA;
+ /**
+ * APN type for Emergency PDN. This is not an IA apn, but is used
+ * for access to carrier services in an emergency call situation.
+ */
+ public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnType {}
+
+ // Possible values for authentication types.
+ /** No authentication type. */
+ public static final int AUTH_TYPE_NONE = 0;
+ /** Authentication type for PAP. */
+ public static final int AUTH_TYPE_PAP = 1;
+ /** Authentication type for CHAP. */
+ public static final int AUTH_TYPE_CHAP = 2;
+ /** Authentication type for PAP or CHAP. */
+ public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+ /** @hide */
+ @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+ AUTH_TYPE_NONE,
+ AUTH_TYPE_PAP,
+ AUTH_TYPE_CHAP,
+ AUTH_TYPE_PAP_OR_CHAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AuthType {}
+
+ // Possible values for protocol.
+ /** Protocol type for IP. */
+ public static final int PROTOCOL_IP = 0;
+ /** Protocol type for IPV6. */
+ public static final int PROTOCOL_IPV6 = 1;
+ /** Protocol type for IPV4V6. */
+ public static final int PROTOCOL_IPV4V6 = 2;
+ /** Protocol type for PPP. */
+ public static final int PROTOCOL_PPP = 3;
+
+ /** @hide */
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ PROTOCOL_IP,
+ PROTOCOL_IPV6,
+ PROTOCOL_IPV4V6,
+ PROTOCOL_PPP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtocolType {}
+
+ // Possible values for MVNO type.
+ /** MVNO type for service provider name. */
+ public static final int MVNO_TYPE_SPN = 0;
+ /** MVNO type for IMSI. */
+ public static final int MVNO_TYPE_IMSI = 1;
+ /** MVNO type for group identifier level 1. */
+ public static final int MVNO_TYPE_GID = 2;
+ /** MVNO type for ICCID. */
+ public static final int MVNO_TYPE_ICCID = 3;
+
+ /** @hide */
+ @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+ MVNO_TYPE_SPN,
+ MVNO_TYPE_IMSI,
+ MVNO_TYPE_GID,
+ MVNO_TYPE_ICCID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MvnoType {}
+
+ static {
+ APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+ APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+ APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+ APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+ APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+ APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+ APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+ APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+ APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+ APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+ APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+ APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+ APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+ APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+ APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+ APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+ APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+ APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+ APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+ APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+ APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+ APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+ APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+ PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+ PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+ PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+ PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+ PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+ PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+ PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+ PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+ PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+ PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+
+ MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+ MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+ MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+ MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+ MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+ MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+ MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+ MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+ MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+ MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+ }
+
private final String mEntryName;
private final String mApnName;
- private final InetAddress mProxy;
- private final int mPort;
- private final URL mMmsc;
- private final InetAddress mMmsProxy;
- private final int mMmsPort;
+ private final InetAddress mProxyAddress;
+ private final int mProxyPort;
+ private final Uri mMmsc;
+ private final InetAddress mMmsProxyAddress;
+ private final int mMmsProxyPort;
private final String mUser;
private final String mPassword;
private final int mAuthType;
- private final List<String> mTypes;
- private final int mTypesBitmap;
+ private final int mApnTypeBitmask;
private final int mId;
private final String mOperatorNumeric;
- private final String mProtocol;
- private final String mRoamingProtocol;
+ private final int mProtocol;
+ private final int mRoamingProtocol;
private final int mMtu;
private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
private final int mWaitTime;
private final int mMaxConnsTime;
- private final String mMvnoType;
+ private final int mMvnoType;
private final String mMvnoMatchData;
private boolean mPermanentFailed = false;
/**
- * Returns the types bitmap of the APN.
- *
- * @return types bitmap of the APN
- * @hide
- */
- public int getTypesBitmap() {
- return mTypesBitmap;
- }
-
- /**
* Returns the MTU size of the mobile interface to which the APN connected.
*
* @return the MTU size of the APN
@@ -211,8 +358,8 @@
*
* @return proxy address.
*/
- public InetAddress getProxy() {
- return mProxy;
+ public InetAddress getProxyAddress() {
+ return mProxyAddress;
}
/**
@@ -220,15 +367,15 @@
*
* @return proxy port
*/
- public int getPort() {
- return mPort;
+ public int getProxyPort() {
+ return mProxyPort;
}
/**
- * Returns the MMSC URL of the APN.
+ * Returns the MMSC Uri of the APN.
*
- * @return MMSC URL.
+ * @return MMSC Uri.
*/
- public URL getMmsc() {
+ public Uri getMmsc() {
return mMmsc;
}
@@ -237,8 +384,8 @@
*
* @return MMS proxy address.
*/
- public InetAddress getMmsProxy() {
- return mMmsProxy;
+ public InetAddress getMmsProxyAddress() {
+ return mMmsProxyAddress;
}
/**
@@ -246,8 +393,8 @@
*
* @return MMS proxy port
*/
- public int getMmsPort() {
- return mMmsPort;
+ public int getMmsProxyPort() {
+ return mMmsProxyPort;
}
/**
@@ -268,21 +415,9 @@
return mPassword;
}
- /** @hide */
- @IntDef({
- AUTH_TYPE_NONE,
- AUTH_TYPE_PAP,
- AUTH_TYPE_CHAP,
- AUTH_TYPE_PAP_OR_CHAP,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AuthType {}
-
/**
* Returns the authentication type of the APN.
*
- * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
- *
* @return authentication type
*/
@AuthType
@@ -290,32 +425,20 @@
return mAuthType;
}
- /** @hide */
- @StringDef({
- TYPE_DEFAULT,
- TYPE_MMS,
- TYPE_SUPL,
- TYPE_DUN,
- TYPE_HIPRI,
- TYPE_FOTA,
- TYPE_IMS,
- TYPE_CBS,
- TYPE_IA,
- TYPE_EMERGENCY
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ApnType {}
-
/**
- * Returns the list of APN types of the APN.
+ * Returns the bitmask of APN types.
*
- * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+ * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+ * MMS-specific connections.
*
- * @return the list of APN types
+ * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+ *
+ * @see Builder#setApnTypeBitmask(int)
+ * @return a bitmask describing the types of the APN
*/
- @ApnType
- public List<String> getTypes() {
- return mTypes;
+ public @ApnType int getApnTypeBitmask() {
+ return mApnTypeBitmask;
}
/**
@@ -328,7 +451,7 @@
}
/**
- * Returns the numeric operator ID for the APN. Usually
+ * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
* {@link android.provider.Telephony.Carriers#MCC} +
* {@link android.provider.Telephony.Carriers#MNC}.
*
@@ -338,37 +461,29 @@
return mOperatorNumeric;
}
- /** @hide */
- @StringDef({
- PROTOCOL_IP,
- PROTOCOL_IPV6,
- PROTOCOL_IPV4V6,
- PROTOCOL_PPP,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ProtocolType {}
-
/**
* Returns the protocol to use to connect to this APN.
*
- * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
- * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
*
+ * @see Builder#setProtocol(int)
* @return the protocol
*/
@ProtocolType
- public String getProtocol() {
+ public int getProtocol() {
return mProtocol;
}
/**
- * Returns the protocol to use to connect to this APN when roaming.
+ * Returns the protocol to use to connect to this APN while the device is roaming.
*
- * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+ * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
*
+ * @see Builder#setRoamingProtocol(int)
* @return the roaming protocol
*/
- public String getRoamingProtocol() {
+ @ProtocolType
+ public int getRoamingProtocol() {
return mRoamingProtocol;
}
@@ -398,41 +513,29 @@
return mNetworkTypeBitmask;
}
- /** @hide */
- @StringDef({
- MVNO_TYPE_SPN,
- MVNO_TYPE_IMSI,
- MVNO_TYPE_GID,
- MVNO_TYPE_ICCID,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MvnoType {}
-
/**
* Returns the MVNO match type for this APN.
*
- * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
- *
+ * @see Builder#setMvnoType(int)
* @return the MVNO match type
*/
@MvnoType
- public String getMvnoType() {
+ public int getMvnoType() {
return mMvnoType;
}
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
- this.mProxy = builder.mProxy;
- this.mPort = builder.mPort;
+ this.mProxyAddress = builder.mProxyAddress;
+ this.mProxyPort = builder.mProxyPort;
this.mMmsc = builder.mMmsc;
- this.mMmsProxy = builder.mMmsProxy;
- this.mMmsPort = builder.mMmsPort;
+ this.mMmsProxyAddress = builder.mMmsProxyAddress;
+ this.mMmsProxyPort = builder.mMmsProxyPort;
this.mUser = builder.mUser;
this.mPassword = builder.mPassword;
this.mAuthType = builder.mAuthType;
- this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
- this.mTypesBitmap = builder.mTypesBitmap;
+ this.mApnTypeBitmask = builder.mApnTypeBitmask;
this.mId = builder.mId;
this.mOperatorNumeric = builder.mOperatorNumeric;
this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
/** @hide */
public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
- String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
- int mmsPort, String user, String password, int authType, List<String> types,
- String protocol, String roamingProtocol, boolean carrierEnabled,
+ String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+ int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+ int protocol, int roamingProtocol, boolean carrierEnabled,
int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
- int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+ int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
return new Builder()
.setId(id)
.setOperatorNumeric(operatorNumeric)
.setEntryName(entryName)
.setApnName(apnName)
- .setProxy(proxy)
- .setPort(port)
+ .setProxyAddress(proxy)
+ .setProxyPort(port)
.setMmsc(mmsc)
- .setMmsProxy(mmsProxy)
- .setMmsPort(mmsPort)
+ .setMmsProxyAddress(mmsProxy)
+ .setMmsProxyPort(mmsPort)
.setUser(user)
.setPassword(password)
.setAuthType(authType)
- .setTypes(types)
+ .setApnTypeBitmask(mApnTypeBitmask)
.setProtocol(protocol)
.setRoamingProtocol(roamingProtocol)
.setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
/** @hide */
public static ApnSetting makeApnSetting(Cursor cursor) {
- String[] types = parseTypes(
+ final int apnTypesBitmask = parseTypes(
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
int networkTypeBitmask = cursor.getInt(
cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
portFromString(cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
- URLFromString(cursor.getString(
+ UriFromString(cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
inetAddressFromString(cursor.getString(
cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
- Arrays.asList(types),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
- cursor.getString(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.ROAMING_PROTOCOL)),
+ apnTypesBitmask,
+ nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+ nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.ROAMING_PROTOCOL)))),
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.CARRIER_ENABLED)) == 1,
networkTypeBitmask,
@@ -531,8 +636,9 @@
cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MAX_CONNS_TIME)),
cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
- cursor.getString(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.MVNO_TYPE)),
+ nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MVNO_TYPE)))),
cursor.getString(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MVNO_MATCH_DATA)));
}
@@ -540,8 +646,8 @@
/** @hide */
public static ApnSetting makeApnSetting(ApnSetting apn) {
return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
- apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
- apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+ apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+ apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
.append(", ").append(mId)
.append(", ").append(mOperatorNumeric)
.append(", ").append(mApnName)
- .append(", ").append(inetAddressToString(mProxy))
- .append(", ").append(URLToString(mMmsc))
- .append(", ").append(inetAddressToString(mMmsProxy))
- .append(", ").append(portToString(mMmsPort))
- .append(", ").append(portToString(mPort))
+ .append(", ").append(inetAddressToString(mProxyAddress))
+ .append(", ").append(UriToString(mMmsc))
+ .append(", ").append(inetAddressToString(mMmsProxyAddress))
+ .append(", ").append(portToString(mMmsProxyPort))
+ .append(", ").append(portToString(mProxyPort))
.append(", ").append(mAuthType).append(", ");
- for (int i = 0; i < mTypes.size(); i++) {
- sb.append(mTypes.get(i));
- if (i < mTypes.size() - 1) {
- sb.append(" | ");
- }
- }
+ final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+ sb.append(TextUtils.join(" | ", types)).append(", ");
sb.append(", ").append(mProtocol);
sb.append(", ").append(mRoamingProtocol);
sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
* @hide
*/
public boolean hasMvnoParams() {
- return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+ return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
}
/** @hide */
- public boolean canHandleType(String type) {
- if (!mCarrierEnabled) return false;
- boolean wildcardable = true;
- if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
- for (String t : mTypes) {
- // DEFAULT handles all, and HIPRI is handled by DEFAULT
- if (t.equalsIgnoreCase(type)
- || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
- || (t.equalsIgnoreCase(TYPE_DEFAULT)
- && type.equalsIgnoreCase(TYPE_HIPRI))) {
- return true;
- }
- }
- return false;
+ public boolean canHandleType(@ApnType int type) {
+ return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
}
// check whether the types of two APN same (even only one type of each APN is same)
private boolean typeSameAny(ApnSetting first, ApnSetting second) {
if (VDBG) {
StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
- for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
- apnType1.append(first.mTypes.get(index1));
- apnType1.append(",");
- }
+ apnType1.append(deParseTypes(first.mApnTypeBitmask));
StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
- for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
- apnType2.append(second.mTypes.get(index1));
- apnType2.append(",");
- }
+ apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
Rlog.d(LOG_TAG, "APN1: is " + apnType1);
Rlog.d(LOG_TAG, "APN2: is " + apnType2);
}
- for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
- for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
- if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
- || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
- || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
- return true;
- }
+ if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+ if (VDBG) {
+ Rlog.d(LOG_TAG, "typeSameAny: return true");
}
+ return true;
}
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+ if (VDBG) {
+ Rlog.d(LOG_TAG, "typeSameAny: return false");
+ }
return false;
}
@@ -655,16 +738,15 @@
&& Objects.equals(mId, other.mId)
&& Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(mApnName, other.mApnName)
- && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mProxyAddress, other.mProxyAddress)
&& Objects.equals(mMmsc, other.mMmsc)
- && Objects.equals(mMmsProxy, other.mMmsProxy)
- && Objects.equals(mMmsPort, other.mMmsPort)
- && Objects.equals(mPort,other.mPort)
+ && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+ && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+ && Objects.equals(mProxyPort,other.mProxyPort)
&& Objects.equals(mUser, other.mUser)
&& Objects.equals(mPassword, other.mPassword)
&& Objects.equals(mAuthType, other.mAuthType)
- && Objects.equals(mTypes, other.mTypes)
- && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
&& Objects.equals(mProtocol, other.mProtocol)
&& Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
return mEntryName.equals(other.mEntryName)
&& Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(mApnName, other.mApnName)
- && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mProxyAddress, other.mProxyAddress)
&& Objects.equals(mMmsc, other.mMmsc)
- && Objects.equals(mMmsProxy, other.mMmsProxy)
- && Objects.equals(mMmsPort, other.mMmsPort)
- && Objects.equals(mPort, other.mPort)
+ && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+ && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+ && Objects.equals(mProxyPort, other.mProxyPort)
&& Objects.equals(mUser, other.mUser)
&& Objects.equals(mPassword, other.mPassword)
&& Objects.equals(mAuthType, other.mAuthType)
- && Objects.equals(mTypes, other.mTypes)
- && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
&& (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
&& (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
&& !other.canHandleType(TYPE_DUN)
&& Objects.equals(this.mApnName, other.mApnName)
&& !typeSameAny(this, other)
- && xorEqualsInetAddress(this.mProxy, other.mProxy)
- && xorEqualsPort(this.mPort, other.mPort)
+ && xorEquals(this.mProxyAddress, other.mProxyAddress)
+ && xorEqualsPort(this.mProxyPort, other.mProxyPort)
&& xorEquals(this.mProtocol, other.mProtocol)
&& xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
&& Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mMvnoType, other.mMvnoType)
&& Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
- && xorEqualsURL(this.mMmsc, other.mMmsc)
- && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
- && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+ && xorEquals(this.mMmsc, other.mMmsc)
+ && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+ && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
&& Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
}
@@ -757,42 +838,23 @@
|| TextUtils.isEmpty(second));
}
- // Equal or one is not specified.
- private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
- return first == null || second == null || first.equals(second);
- }
-
- // Equal or one is not specified.
- private boolean xorEqualsURL(URL first, URL second) {
+ // Equal or one is not null.
+ private boolean xorEquals(Object first, Object second) {
return first == null || second == null || first.equals(second);
}
// Equal or one is not specified.
private boolean xorEqualsPort(int first, int second) {
- return first == -1 || second == -1 || Objects.equals(first, second);
+ return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+ || Objects.equals(first, second);
}
- // Helper function to convert APN string into a 32-bit bitmask.
- private static int getApnBitmask(String apn) {
- switch (apn) {
- case TYPE_DEFAULT: return ApnTypes.DEFAULT;
- case TYPE_MMS: return ApnTypes.MMS;
- case TYPE_SUPL: return ApnTypes.SUPL;
- case TYPE_DUN: return ApnTypes.DUN;
- case TYPE_HIPRI: return ApnTypes.HIPRI;
- case TYPE_FOTA: return ApnTypes.FOTA;
- case TYPE_IMS: return ApnTypes.IMS;
- case TYPE_CBS: return ApnTypes.CBS;
- case TYPE_IA: return ApnTypes.IA;
- case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
- case TYPE_ALL: return ApnTypes.ALL;
- default: return ApnTypes.NONE;
- }
- }
-
- private String deParseTypes(List<String> types) {
- if (types == null) {
- return null;
+ private String deParseTypes(int apnTypeBitmask) {
+ List<String> types = new ArrayList<>();
+ for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+ if ((apnTypeBitmask & type) == type) {
+ types.add(APN_TYPE_INT_MAP.get(type));
+ }
}
return TextUtils.join(",", types);
}
@@ -808,21 +870,25 @@
apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
- apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
- apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
- apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
- apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
- apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
- ? "" : inetAddressToString(mMmsProxy));
+ apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+ : inetAddressToString(mProxyAddress));
+ apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+ apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+ apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+ apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+ ? "" : inetAddressToString(mMmsProxyAddress));
apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
- String apnType = deParseTypes(mTypes);
+ String apnType = deParseTypes(mApnTypeBitmask);
apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
- apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
- apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+ apnValue.put(Telephony.Carriers.PROTOCOL,
+ nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+ apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+ nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
- apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+ apnValue.put(Telephony.Carriers.MVNO_TYPE,
+ nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
return apnValue;
@@ -830,32 +896,31 @@
/**
* @param types comma delimited list of APN types
- * @return array of APN types
+ * @return bitmask of APN types
* @hide
*/
- public static String[] parseTypes(String types) {
- String[] result;
- // If unset, set to DEFAULT.
+ public static int parseTypes(String types) {
+ // If unset, set to ALL.
if (TextUtils.isEmpty(types)) {
- result = new String[1];
- result[0] = TYPE_ALL;
+ return TYPE_ALL_BUT_IA;
} else {
- result = types.split(",");
- }
- return result;
- }
-
- private static URL URLFromString(String url) {
- try {
- return TextUtils.isEmpty(url) ? null : new URL(url);
- } catch (MalformedURLException e) {
- Log.e(LOG_TAG, "Can't parse URL from string.");
- return null;
+ int result = 0;
+ for (String str : types.split(",")) {
+ Integer type = APN_TYPE_STRING_MAP.get(str);
+ if (type != null) {
+ result |= type;
+ }
+ }
+ return result;
}
}
- private static String URLToString(URL url) {
- return url == null ? "" : url.toString();
+ private static Uri UriFromString(String uri) {
+ return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+ }
+
+ private static String UriToString(Uri uri) {
+ return uri == null ? "" : uri.toString();
}
private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
}
private static int portFromString(String strPort) {
- int port = -1;
+ int port = NO_PORT_SPECIFIED;
if (!TextUtils.isEmpty(strPort)) {
try {
port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
}
private static String portToString(int port) {
- return port == -1 ? "" : Integer.toString(port);
+ return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
}
// Implement Parcelable.
@@ -916,19 +981,19 @@
dest.writeString(mOperatorNumeric);
dest.writeString(mEntryName);
dest.writeString(mApnName);
- dest.writeValue(mProxy);
- dest.writeInt(mPort);
+ dest.writeValue(mProxyAddress);
+ dest.writeInt(mProxyPort);
dest.writeValue(mMmsc);
- dest.writeValue(mMmsProxy);
- dest.writeInt(mMmsPort);
+ dest.writeValue(mMmsProxyAddress);
+ dest.writeInt(mMmsProxyPort);
dest.writeString(mUser);
dest.writeString(mPassword);
dest.writeInt(mAuthType);
- dest.writeStringArray(mTypes.toArray(new String[0]));
- dest.writeString(mProtocol);
- dest.writeString(mRoamingProtocol);
+ dest.writeInt(mApnTypeBitmask);
+ dest.writeInt(mProtocol);
+ dest.writeInt(mRoamingProtocol);
dest.writeInt(mCarrierEnabled ? 1: 0);
- dest.writeString(mMvnoType);
+ dest.writeInt(mMvnoType);
dest.writeInt(mNetworkTypeBitmask);
}
@@ -939,23 +1004,23 @@
final String apnName = in.readString();
final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
final int port = in.readInt();
- final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+ final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
final int mmsPort = in.readInt();
final String user = in.readString();
final String password = in.readString();
final int authType = in.readInt();
- final List<String> types = Arrays.asList(in.readStringArray());
- final String protocol = in.readString();
- final String roamingProtocol = in.readString();
+ final int apnTypesBitmask = in.readInt();
+ final int protocol = in.readInt();
+ final int roamingProtocol = in.readInt();
final boolean carrierEnabled = in.readInt() > 0;
- final String mvnoType = in.readString();
+ final int mvnoType = in.readInt();
final int networkTypeBitmask = in.readInt();
return makeApnSetting(id, operatorNumeric, entryName, apnName,
- proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
- roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
- 0, 0, 0, 0, mvnoType, null);
+ proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+ protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+ 0, 0, 0, 0, mvnoType, null);
}
public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
}
};
- /**
- * APN types for data connections. These are usage categories for an APN
- * entry. One APN entry may support multiple APN types, eg, a single APN
- * may service regular internet traffic ("default") as well as MMS-specific
- * connections.<br/>
- * ALL is a special type to indicate that this APN entry can
- * service all data connections.
- */
- public static final String TYPE_ALL = "*";
- /** APN type for default data traffic */
- public static final String TYPE_DEFAULT = "default";
- /** APN type for MMS traffic */
- public static final String TYPE_MMS = "mms";
- /** APN type for SUPL assisted GPS */
- public static final String TYPE_SUPL = "supl";
- /** APN type for DUN traffic */
- public static final String TYPE_DUN = "dun";
- /** APN type for HiPri traffic */
- public static final String TYPE_HIPRI = "hipri";
- /** APN type for FOTA */
- public static final String TYPE_FOTA = "fota";
- /** APN type for IMS */
- public static final String TYPE_IMS = "ims";
- /** APN type for CBS */
- public static final String TYPE_CBS = "cbs";
- /** APN type for IA Initial Attach APN */
- public static final String TYPE_IA = "ia";
- /** APN type for Emergency PDN. This is not an IA apn, but is used
- * for access to carrier services in an emergency call situation. */
- public static final String TYPE_EMERGENCY = "emergency";
- /**
- * Array of all APN types
- *
- * @hide
- */
- public static final String[] ALL_TYPES = {
- TYPE_DEFAULT,
- TYPE_MMS,
- TYPE_SUPL,
- TYPE_DUN,
- TYPE_HIPRI,
- TYPE_FOTA,
- TYPE_IMS,
- TYPE_CBS,
- TYPE_IA,
- TYPE_EMERGENCY
- };
-
- // Possible values for authentication types.
- public static final int AUTH_TYPE_NONE = 0;
- public static final int AUTH_TYPE_PAP = 1;
- public static final int AUTH_TYPE_CHAP = 2;
- public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
- // Possible values for protocol.
- public static final String PROTOCOL_IP = "IP";
- public static final String PROTOCOL_IPV6 = "IPV6";
- public static final String PROTOCOL_IPV4V6 = "IPV4V6";
- public static final String PROTOCOL_PPP = "PPP";
-
- // Possible values for MVNO type.
- public static final String MVNO_TYPE_SPN = "spn";
- public static final String MVNO_TYPE_IMSI = "imsi";
- public static final String MVNO_TYPE_GID = "gid";
- public static final String MVNO_TYPE_ICCID = "iccid";
+ private static int nullToNotInMapInt(Integer value) {
+ return value == null ? NOT_IN_MAP_INT : value;
+ }
public static class Builder{
private String mEntryName;
private String mApnName;
- private InetAddress mProxy;
- private int mPort = -1;
- private URL mMmsc;
- private InetAddress mMmsProxy;
- private int mMmsPort = -1;
+ private InetAddress mProxyAddress;
+ private int mProxyPort = NO_PORT_SPECIFIED;
+ private Uri mMmsc;
+ private InetAddress mMmsProxyAddress;
+ private int mMmsProxyPort = NO_PORT_SPECIFIED;
private String mUser;
private String mPassword;
private int mAuthType;
- private List<String> mTypes;
- private int mTypesBitmap;
+ private int mApnTypeBitmask;
private int mId;
private String mOperatorNumeric;
- private String mProtocol;
- private String mRoamingProtocol;
+ private int mProtocol = NOT_IN_MAP_INT;
+ private int mRoamingProtocol = NOT_IN_MAP_INT;
private int mMtu;
private int mNetworkTypeBitmask;
private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
private int mMaxConns;
private int mWaitTime;
private int mMaxConnsTime;
- private String mMvnoType;
+ private int mMvnoType = NOT_IN_MAP_INT;
private String mMvnoMatchData;
/**
@@ -1182,8 +1184,8 @@
*
* @param proxy the proxy address to set for the APN
*/
- public Builder setProxy(InetAddress proxy) {
- this.mProxy = proxy;
+ public Builder setProxyAddress(InetAddress proxy) {
+ this.mProxyAddress = proxy;
return this;
}
@@ -1192,17 +1194,17 @@
*
* @param port the proxy port to set for the APN
*/
- public Builder setPort(int port) {
- this.mPort = port;
+ public Builder setProxyPort(int port) {
+ this.mProxyPort = port;
return this;
}
/**
- * Sets the MMSC URL of the APN.
+ * Sets the MMSC Uri of the APN.
*
- * @param mmsc the MMSC URL to set for the APN
+ * @param mmsc the MMSC Uri to set for the APN
*/
- public Builder setMmsc(URL mmsc) {
+ public Builder setMmsc(Uri mmsc) {
this.mMmsc = mmsc;
return this;
}
@@ -1212,8 +1214,8 @@
*
* @param mmsProxy the MMS proxy address to set for the APN
*/
- public Builder setMmsProxy(InetAddress mmsProxy) {
- this.mMmsProxy = mmsProxy;
+ public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+ this.mMmsProxyAddress = mmsProxy;
return this;
}
@@ -1222,8 +1224,8 @@
*
* @param mmsPort the MMS proxy port to set for the APN
*/
- public Builder setMmsPort(int mmsPort) {
- this.mMmsPort = mmsPort;
+ public Builder setMmsProxyPort(int mmsPort) {
+ this.mMmsProxyPort = mmsPort;
return this;
}
@@ -1251,8 +1253,6 @@
/**
* Sets the authentication type of the APN.
*
- * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
- *
* @param authType the authentication type to set for the APN
*/
public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
}
/**
- * Sets the list of APN types of the APN.
+ * Sets the bitmask of APN types.
*
- * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+ * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+ * MMS-specific connections.
*
- * @param types the list of APN types to set for the APN
+ * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+ *
+ * @param apnTypeBitmask a bitmask describing the types of the APN
*/
- public Builder setTypes(@ApnType List<String> types) {
- this.mTypes = types;
- int apnBitmap = 0;
- for (int i = 0; i < mTypes.size(); i++) {
- mTypes.set(i, mTypes.get(i).toLowerCase());
- apnBitmap |= getApnBitmask(mTypes.get(i));
- }
- this.mTypesBitmap = apnBitmap;
+ public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+ this.mApnTypeBitmask = apnTypeBitmask;
return this;
}
/**
- * Set the numeric operator ID for the APN.
+ * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+ * {@link android.provider.Telephony.Carriers#MCC} +
+ * {@link android.provider.Telephony.Carriers#MNC}.
*
* @param operatorNumeric the numeric operator ID to set for this entry
*/
@@ -1291,22 +1291,23 @@
/**
* Sets the protocol to use to connect to this APN.
*
- * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
- * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
*
* @param protocol the protocol to set to use to connect to this APN
*/
- public Builder setProtocol(@ProtocolType String protocol) {
+ public Builder setProtocol(@ProtocolType int protocol) {
this.mProtocol = protocol;
return this;
}
/**
- * Sets the protocol to use to connect to this APN when roaming.
+ * Sets the protocol to use to connect to this APN when the device is roaming.
+ *
+ * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
*
* @param roamingProtocol the protocol to set to use to connect to this APN when roaming
*/
- public Builder setRoamingProtocol(String roamingProtocol) {
+ public Builder setRoamingProtocol(@ProtocolType int roamingProtocol) {
this.mRoamingProtocol = roamingProtocol;
return this;
}
@@ -1334,16 +1335,25 @@
/**
* Sets the MVNO match type for this APN.
*
- * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
- *
* @param mvnoType the MVNO match type to set for this APN
*/
- public Builder setMvnoType(@MvnoType String mvnoType) {
+ public Builder setMvnoType(@MvnoType int mvnoType) {
this.mMvnoType = mvnoType;
return this;
}
+ /**
+ * Builds {@link ApnSetting} from this builder.
+ *
+ * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+ * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+ * {@link ApnSetting} built from this builder otherwise.
+ */
public ApnSetting build() {
+ if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+ || TextUtils.isEmpty(mEntryName)) {
+ return null;
+ }
return new ApnSetting(this);
}
}
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 88db22b..edf3b08 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.UiccAccessRule;
@@ -26,7 +27,14 @@
import com.android.internal.util.Preconditions;
-/** Information about a subscription which is available for download. */
+/**
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
+ *
+ * <p>For example, a DownloadableSubscription can be created through an activation code parsed from
+ * a QR code. A server address can be parsed from the activation code to download more information
+ * about the profile, such as carrier name, access rules, etc.
+ */
public final class DownloadableSubscription implements Parcelable {
public static final Creator<DownloadableSubscription> CREATOR =
@@ -136,7 +144,15 @@
/**
* Create a DownloadableSubscription for the given activation code.
*
- * @param encodedActivationCode the activation code to use. Must not be null.
+ * <p>This fills the encodedActivationCode field. Other fields like confirmationCode,
+ * carrierName and accessRules may be filled in the implementation of
+ * {@code android.service.euicc.EuiccService} if exists.
+ *
+ * @param encodedActivationCode the activation code to use. An activation code can be parsed
+ * from a user scanned QR code. The format of activation code is defined in SGP.22. For
+ * example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For detail, see
+ * {@code com.android.euicc.data.ActivationCode}. Must not be null.
+ *
* @return the {@link DownloadableSubscription} which may be passed to
* {@link EuiccManager#downloadSubscription}.
*/
@@ -157,6 +173,9 @@
/**
* Returns the confirmation code.
+ *
+ * <p>As an example, the confirmation code can be input by the user through a carrier app or the
+ * UI component of the eUICC local profile assistant (LPA) application.
*/
@Nullable
public String getConfirmationCode() {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 8279123..d6f94da 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -142,11 +142,9 @@
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
/**
- * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+ * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
* callbacks providing the downloadable subscription metadata.
- * @hide
*/
- @SystemApi
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 92d62da..d03f63d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -197,7 +197,9 @@
* platform will deliver the message to the messages database and notify the IMS provider of the
* result by calling {@link #acknowledgeSms(int, int, int)}.
*
- * This method must not be called before {@link #onReady()} is called.
+ * This method must not be called before {@link #onReady()} is called or the call will fail. If
+ * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
+ * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
*
* @param token unique token generated by IMS providers that the platform will use to trigger
* callbacks for this message.
@@ -215,7 +217,14 @@
mListener.onSmsReceived(token, format, pdu);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
- acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+ DELIVER_STATUS_ERROR_GENERIC);
+ } else {
+ Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+ }
}
}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 0000000..4301cb1
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadProgressListener {
+ /**
+ * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
+ *
+ * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+ * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
+ * the request may result in many files being downloaded and the client
+ * may not have been able to get a list of them in advance.
+ * @param currentDownloadSize is the current amount downloaded.
+ * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+ * This may be different from the decoded final size, but is useful in gauging download
+ * progress.
+ * @param currentDecodedSize is the number of bytes that have been decoded.
+ * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+ */
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize) {
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 602c796..9e3302b 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
@@ -184,6 +185,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public Builder setServiceId(String serviceId) {
fileServiceId = serviceId;
return this;
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
deleted file mode 100644
index 9f60cc3..0000000
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 android.telephony.mbms;
-
-import android.annotation.IntDef;
-import android.telephony.MbmsDownloadSession;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A optional listener class used by download clients to track progress. Apps should extend this
- * class and pass an instance into
- * {@link MbmsDownloadSession#download(DownloadRequest)}
- *
- * This is optionally specified when requesting a download and will only be called while the app
- * is running.
- */
-public class DownloadStateCallback {
-
- /**
- * Bitmask flags used for filtering out callback methods. Used when constructing the
- * DownloadStateCallback as an optional parameter.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
- public @interface FilterFlag {}
-
- /**
- * Receive all callbacks.
- * Default value.
- */
- public static final int ALL_UPDATES = 0x00;
- /**
- * Receive callbacks for {@link #onProgressUpdated}.
- */
- public static final int PROGRESS_UPDATES = 0x01;
- /**
- * Receive callbacks for {@link #onStateUpdated}.
- */
- public static final int STATE_UPDATES = 0x02;
-
- private final int mCallbackFilterFlags;
-
- /**
- * Creates a DownloadStateCallback that will receive all callbacks.
- */
- public DownloadStateCallback() {
- mCallbackFilterFlags = ALL_UPDATES;
- }
-
- /**
- * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
- * via the filterFlags parameter.
- * @param filterFlags A bitmask of filter flags that will specify which callback this instance
- * is interested in.
- */
- public DownloadStateCallback(int filterFlags) {
- mCallbackFilterFlags = filterFlags;
- }
-
- /**
- * Return the currently set filter flags.
- * @return An integer containing the bitmask of flags that this instance is interested in.
- * @hide
- */
- public int getCallbackFilterFlags() {
- return mCallbackFilterFlags;
- }
-
- /**
- * Returns true if a filter flag is set for a particular callback method. If the flag is set,
- * the callback will be delivered to the listening process.
- * @param flag A filter flag specifying whether or not a callback method is registered to
- * receive callbacks.
- * @return true if registered to receive callbacks in the listening process, false if not.
- */
- public final boolean isFilterFlagSet(@FilterFlag int flag) {
- if (mCallbackFilterFlags == ALL_UPDATES) {
- return true;
- }
- return (mCallbackFilterFlags & flag) > 0;
- }
-
- /**
- * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
- *
- * @param request a {@link DownloadRequest}, indicating which download is being referenced.
- * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
- * the request may result in many files being downloaded and the client
- * may not have been able to get a list of them in advance.
- * @param currentDownloadSize is the current amount downloaded.
- * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
- * This may be different from the decoded final size, but is useful in gauging download
- * progress.
- * @param currentDecodedSize is the number of bytes that have been decoded.
- * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
- */
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
- int currentDownloadSize, int fullDownloadSize,
- int currentDecodedSize, int fullDecodedSize) {
- }
-
- /**
- * Gives download state callbacks for a file in a {@link DownloadRequest}.
- *
- * @param request a {@link DownloadRequest}, indicating which download is being referenced.
- * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
- * the request may result in many files being downloaded and the client
- * may not have been able to get a list of them in advance.
- * @param state The current state of the download.
- */
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- }
-}
diff --git a/telephony/java/android/telephony/mbms/DownloadStatusListener.java b/telephony/java/android/telephony/mbms/DownloadStatusListener.java
new file mode 100644
index 0000000..ca77932
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/DownloadStatusListener.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsDownloadSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadStatusListener {
+ /**
+ * Gives download status callbacks for a file in a {@link DownloadRequest}.
+ *
+ * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+ * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
+ * the request may result in many files being downloaded and the client
+ * may not have been able to get a list of them in advance.
+ * @param status The current status of the download.
+ */
+ public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int status) {
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
similarity index 90%
rename from telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
rename to telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
index cebc70d..d0adcb5 100755
--- a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
@@ -23,7 +23,7 @@
* The optional interface used by download clients to track progress.
* @hide
*/
-interface IDownloadStateCallback
+interface IDownloadProgressListener
{
/**
* Gives progress callbacks for a given DownloadRequest. Includes a FileInfo
@@ -32,6 +32,4 @@
void onProgressUpdated(in DownloadRequest request, in FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize,
int currentDecodedSize, int fullDecodedSize);
-
- void onStateUpdated(in DownloadRequest request, in FileInfo fileInfo, int state);
}
diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
new file mode 100755
index 0000000..799290a
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 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.mbms;
+
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.FileInfo;
+
+/**
+ * The optional interface used by download clients to track download status.
+ * @hide
+ */
+interface IDownloadStatusListener
+{
+ void onStatusUpdated(in DownloadRequest request, in FileInfo fileInfo, int status);
+}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
similarity index 62%
rename from telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
rename to telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
index f30ae27..403f758 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -24,13 +24,14 @@
/**
* @hide
*/
-public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
+public class InternalDownloadProgressListener extends IDownloadProgressListener.Stub {
private final Executor mExecutor;
- private final DownloadStateCallback mAppCallback;
+ private final DownloadProgressListener mAppListener;
private volatile boolean mIsStopped = false;
- public InternalDownloadStateCallback(DownloadStateCallback appCallback, Executor executor) {
- mAppCallback = appCallback;
+ public InternalDownloadProgressListener(DownloadProgressListener appListener,
+ Executor executor) {
+ mAppListener = appListener;
mExecutor = executor;
}
@@ -47,7 +48,7 @@
public void run() {
long token = Binder.clearCallingIdentity();
try {
- mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
fullDownloadSize, currentDecodedSize, fullDecodedSize);
} finally {
Binder.restoreCallingIdentity(token);
@@ -56,26 +57,6 @@
});
}
- @Override
- public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
- final int state) throws RemoteException {
- if (mIsStopped) {
- return;
- }
-
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- long token = Binder.clearCallingIdentity();
- try {
- mAppCallback.onStateUpdated(request, fileInfo, state);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- });
- }
-
public void stop() {
mIsStopped = true;
}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index c2a79d8..2916f81 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -17,7 +17,6 @@
package android.telephony.mbms;
import android.os.Binder;
-import android.os.RemoteException;
import java.util.List;
import java.util.concurrent.Executor;
@@ -36,7 +35,7 @@
}
@Override
- public void onError(final int errorCode, final String message) throws RemoteException {
+ public void onError(final int errorCode, final String message) {
if (mIsStopped) {
return;
}
@@ -55,7 +54,7 @@
}
@Override
- public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+ public void onFileServicesUpdated(final List<FileServiceInfo> services) {
if (mIsStopped) {
return;
}
@@ -74,7 +73,7 @@
}
@Override
- public void onMiddlewareReady() throws RemoteException {
+ public void onMiddlewareReady() {
if (mIsStopped) {
return;
}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
new file mode 100644
index 0000000..ad6bb54
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
@@ -0,0 +1,61 @@
+/*
+ * 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.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStatusListener extends IDownloadStatusListener.Stub {
+ private final Executor mExecutor;
+ private final DownloadStatusListener mAppListener;
+ private volatile boolean mIsStopped = false;
+
+ public InternalDownloadStatusListener(DownloadStatusListener appCallback, Executor executor) {
+ mAppListener = appCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onStatusUpdated(final DownloadRequest request, final FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus final int status) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppListener.onStatusUpdated(request, fileInfo, status);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index b0c00c6..dd1061f 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -31,6 +31,8 @@
import android.telephony.mbms.vendor.VendorUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@@ -268,7 +270,10 @@
Uri finalLocation;
try {
- finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination);
+ String relativeLocation = getFileRelativePath(request.getSourceUri().getPath(),
+ completedFileInfo.getUri().getPath());
+ finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination,
+ relativeLocation);
} catch (IOException e) {
Log.w(LOG_TAG, "Failed to move temp file to final destination");
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -297,7 +302,9 @@
for (Uri tempFileUri : tempFiles) {
if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
File tempFile = new File(tempFileUri.getSchemeSpecificPart());
- tempFile.delete();
+ if (!tempFile.delete()) {
+ Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+ }
}
}
}
@@ -440,7 +447,8 @@
/*
* Moves a tempfile located at fromPath to its final home where the app wants it
*/
- private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException {
+ private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
+ String relativeLocation) throws IOException {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
" does not have a file scheme");
@@ -448,16 +456,46 @@
}
Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
- if (!Files.isDirectory(appSpecifiedPath)) {
- Files.createDirectory(appSpecifiedPath);
+ Path toFile = appSpecifiedPath.resolve(relativeLocation);
+
+ if (!Files.isDirectory(toFile.getParent())) {
+ Files.createDirectories(toFile.getParent());
}
- // TODO: do we want to support directory trees within the download directory?
- Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
+ Path result = Files.move(fromFile, toFile,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
return Uri.fromFile(result.toFile());
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getFileRelativePath(String sourceUriPath, String fileInfoPath) {
+ if (sourceUriPath.endsWith("*")) {
+ // This is a wildcard path. Strip the last path component and use that as the root of
+ // the relative path.
+ int lastSlash = sourceUriPath.lastIndexOf('/');
+ sourceUriPath = sourceUriPath.substring(0, lastSlash);
+ }
+ if (!fileInfoPath.startsWith(sourceUriPath)) {
+ Log.e(LOG_TAG, "File location specified in FileInfo does not match the source URI."
+ + " source: " + sourceUriPath + " fileinfo path: " + fileInfoPath);
+ return null;
+ }
+ if (fileInfoPath.length() == sourceUriPath.length()) {
+ // This is the single-file download case. Return the name of the file so that the
+ // receiver puts the file directly into the dest directory.
+ return sourceUriPath.substring(sourceUriPath.lastIndexOf('/') + 1);
+ }
+
+ String prefixOmittedPath = fileInfoPath.substring(sourceUriPath.length());
+ if (prefixOmittedPath.startsWith("/")) {
+ prefixOmittedPath = prefixOmittedPath.substring(1);
+ }
+ return prefixOmittedPath;
+ }
+
private static boolean verifyTempFilePath(Context context, String serviceId,
Uri filePath) {
if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
@@ -474,6 +512,8 @@
if (!MbmsUtils.isContainedIn(
MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+ Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+ " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
return false;
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
index 77dea6f..5003b57 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -16,8 +16,11 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.telephony.MbmsDownloadSession;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -25,6 +28,26 @@
* cell-broadcast.
*/
public class MbmsDownloadSessionCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+ MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+ MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+ MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+ MbmsErrors.DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+ MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+ MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO}, prefix = { "ERROR_" })
+ private @interface DownloadError{}
/**
* Indicates that the middleware has encountered an asynchronous error.
@@ -32,7 +55,7 @@
* @param message A message, intended for debugging purposes, describing the error in further
* detail.
*/
- public void onError(int errorCode, String message) {
+ public void onError(@DownloadError int errorCode, String message) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index b5fec44..7c4321b 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -19,6 +19,13 @@
import android.telephony.MbmsStreamingSession;
public class MbmsErrors {
+ /**
+ * Indicates that the middleware has sent an error code that is not defined in the version of
+ * the SDK targeted by your app. This is an illegal value for the middleware to return -- it
+ * should only ever be generated by the framework.
+ */
+ public static final int UNKNOWN = -1;
+
/** Indicates that the operation was successful. */
public static final int SUCCESS = 0;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 6e03957..1bdb20bf 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,11 +16,13 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
-import android.os.Handler;
import android.telephony.MbmsStreamingSession;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.concurrent.Executor;
@@ -30,13 +32,34 @@
* {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
*/
public class MbmsStreamingSessionCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+ MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+ MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+ MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+ MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+ MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+ MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+ private @interface StreamingError{}
+
/**
* Called by the middleware when it has detected an error condition. The possible error codes
* are listed in {@link MbmsErrors}.
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(int errorCode, @Nullable String message) {
+ public void onError(@StreamingError int errorCode, @Nullable String message) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index ef317ee..06b2120 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -130,8 +130,12 @@
* Returns a File linked to the directory used to store temp files for this file service
*/
public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
+ // Replace all non-alphanumerics/underscores with an underscore. Some filesystems don't
+ // like special characters.
+ String sanitizedServiceId = serviceId.replaceAll("[^a-zA-Z0-9_]", "_");
+
File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
- return new File(embmsTempFileDir, serviceId);
+ return new File(embmsTempFileDir, sanitizedServiceId);
}
}
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index 0903824..c265db6 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,13 +16,34 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.annotation.Nullable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A callback class for use when the application is actively streaming content. The middleware
* will provide updates on the status of the stream via this callback.
*/
public class StreamingServiceCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+ MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+ MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+ MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+ MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+ MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+ private @interface StreamingServiceError{}
/**
* Indicates broadcast signal strength is not available for this service.
@@ -39,7 +60,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(int errorCode, @Nullable String message) {
+ public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 7d9845f..445087fb 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -20,8 +20,9 @@
import android.net.Uri;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
import android.telephony.mbms.IMbmsDownloadSessionCallback;
-import android.telephony.mbms.IDownloadStateCallback;
/**
* @hide
@@ -36,11 +37,17 @@
int download(in DownloadRequest downloadRequest);
- int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener,
- int flags);
+ int addStatusListener(in DownloadRequest downloadRequest,
+ IDownloadStatusListener listener);
- int unregisterStateCallback(in DownloadRequest downloadRequest,
- IDownloadStateCallback listener);
+ int removeStatusListener(in DownloadRequest downloadRequest,
+ IDownloadStatusListener listener);
+
+ int addProgressListener(in DownloadRequest downloadRequest,
+ IDownloadProgressListener listener);
+
+ int removeProgressListener(in DownloadRequest downloadRequest,
+ IDownloadProgressListener listener);
List<DownloadRequest> listPendingDownloads(int subscriptionId);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 86b1b7a..a9f10b1 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -24,11 +24,13 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadStateCallback;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
import android.telephony.mbms.IMbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsErrors;
@@ -45,47 +47,50 @@
@SystemApi
@TestApi
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
- private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+ private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap =
+ new HashMap<>();
+ private final Map<IBinder, DownloadProgressListener> mDownloadProgressListenerBinderMap =
+ new HashMap<>();
private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+ private abstract static class VendorDownloadStatusListener extends DownloadStatusListener {
+ private final IDownloadStatusListener mListener;
+ public VendorDownloadStatusListener(IDownloadStatusListener listener) {
+ mListener = listener;
+ }
- // Filters the DownloadStateCallbacks by its configuration from the app.
- private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+ @Override
+ public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ try {
+ mListener.onStatusUpdated(request, fileInfo, state);
+ } catch (RemoteException e) {
+ onRemoteException(e);
+ }
+ }
- private final IDownloadStateCallback mCallback;
- public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
- super(callbackFlags);
- mCallback = callback;
+ protected abstract void onRemoteException(RemoteException e);
+ }
+
+ private abstract static class VendorDownloadProgressListener extends DownloadProgressListener {
+ private final IDownloadProgressListener mListener;
+
+ public VendorDownloadProgressListener(IDownloadProgressListener listener) {
+ mListener = listener;
}
@Override
public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
int fullDecodedSize) {
- if (!isFilterFlagSet(PROGRESS_UPDATES)) {
- return;
- }
try {
- mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ mListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
fullDownloadSize, currentDecodedSize, fullDecodedSize);
} catch (RemoteException e) {
onRemoteException(e);
}
}
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- if (!isFilterFlagSet(STATE_UPDATES)) {
- return;
- }
- try {
- mCallback.onStateUpdated(request, fileInfo, state);
- } catch (RemoteException e) {
- onRemoteException(e);
- }
- }
-
protected abstract void onRemoteException(RemoteException e);
}
@@ -125,6 +130,10 @@
@Override
public void onError(int errorCode, String message) {
try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
callback.onError(errorCode, message);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
@@ -223,71 +232,70 @@
}
/**
- * Registers a download state callbacks for the provided {@link DownloadRequest}.
+ * Registers a download status listener for the provided {@link DownloadRequest}.
*
- * This method is called by the app when it wants to request updates on the progress or
- * status of the download.
+ * This method is called by the app when it wants to request updates on the status of
+ * the download.
*
* If the middleware is not aware of a download having been requested with the provided
- *
* {@link DownloadRequest} in the past,
* {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
* must be returned.
*
* @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
* for which progress updates are being requested.
- * @param callback The callback object to use.
+ * @param listener The listener object to use.
*/
- public int registerStateCallback(DownloadRequest downloadRequest,
- DownloadStateCallback callback) throws RemoteException {
+ public int addStatusListener(DownloadRequest downloadRequest,
+ DownloadStatusListener listener) throws RemoteException {
return 0;
}
/**
- * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * Actual AIDL implementation -- hides the listener AIDL from the API.
* @hide
*/
@Override
- public final int registerStateCallback(final DownloadRequest downloadRequest,
- final IDownloadStateCallback callback, int flags) throws RemoteException {
+ public final int addStatusListener(final DownloadRequest downloadRequest,
+ final IDownloadStatusListener listener) throws RemoteException {
final int uid = Binder.getCallingUid();
if (downloadRequest == null) {
throw new NullPointerException("Download request must not be null");
}
- if (callback == null) {
+ if (listener == null) {
throw new NullPointerException("Callback must not be null");
}
- DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
+ DownloadStatusListener exposedCallback = new VendorDownloadStatusListener(listener) {
@Override
protected void onRemoteException(RemoteException e) {
onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
}
};
- int result = registerStateCallback(downloadRequest, exposedCallback);
+ int result = addStatusListener(downloadRequest, exposedCallback);
if (result == MbmsErrors.SUCCESS) {
DeathRecipient deathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
- mDownloadCallbackBinderMap.remove(callback.asBinder());
- mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+ mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+ mDownloadCallbackDeathRecipients.remove(listener.asBinder());
}
};
- mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
- callback.asBinder().linkToDeath(deathRecipient, 0);
- mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+ mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+ listener.asBinder().linkToDeath(deathRecipient, 0);
+ mDownloadStatusListenerBinderMap.put(listener.asBinder(), exposedCallback);
}
return result;
}
/**
- * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+ * Un-registers a download status listener for the provided {@link DownloadRequest}.
*
- * This method is called by the app when it no longer wants to request updates on the
+ * This method is called by the app when it no longer wants to request status updates on the
* download.
*
* If the middleware is not aware of a download having been requested with the provided
@@ -296,45 +304,157 @@
* must be returned.
*
* @param downloadRequest The {@link DownloadRequest} that was used to register the callback
- * @param callback The callback object that
- * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+ * @param listener The callback object that
+ * {@link #addStatusListener(DownloadRequest, DownloadStatusListener)}
* was called with.
*/
- public int unregisterStateCallback(DownloadRequest downloadRequest,
- DownloadStateCallback callback) throws RemoteException {
+ public int removeStatusListener(DownloadRequest downloadRequest,
+ DownloadStatusListener listener) throws RemoteException {
return 0;
}
/**
- * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * Actual AIDL implementation -- hides the listener AIDL from the API.
* @hide
*/
- @Override
- public final int unregisterStateCallback(
- final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+ public final int removeStatusListener(
+ final DownloadRequest downloadRequest, final IDownloadStatusListener listener)
throws RemoteException {
if (downloadRequest == null) {
throw new NullPointerException("Download request must not be null");
}
- if (callback == null) {
+ if (listener == null) {
throw new NullPointerException("Callback must not be null");
}
DeathRecipient deathRecipient =
- mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+ mDownloadCallbackDeathRecipients.remove(listener.asBinder());
if (deathRecipient == null) {
- throw new IllegalArgumentException("Unknown callback");
+ throw new IllegalArgumentException("Unknown listener");
}
- callback.asBinder().unlinkToDeath(deathRecipient, 0);
+ listener.asBinder().unlinkToDeath(deathRecipient, 0);
- DownloadStateCallback exposedCallback =
- mDownloadCallbackBinderMap.remove(callback.asBinder());
+ DownloadStatusListener exposedCallback =
+ mDownloadStatusListenerBinderMap.remove(listener.asBinder());
if (exposedCallback == null) {
- throw new IllegalArgumentException("Unknown callback");
+ throw new IllegalArgumentException("Unknown listener");
}
- return unregisterStateCallback(downloadRequest, exposedCallback);
+ return removeStatusListener(downloadRequest, exposedCallback);
+ }
+
+ /**
+ * Registers a download progress listener for the provided {@link DownloadRequest}.
+ *
+ * This method is called by the app when it wants to request updates on the progress of
+ * the download.
+ *
+ * If the middleware is not aware of a download having been requested with the provided
+ * {@link DownloadRequest} in the past,
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+ * must be returned.
+ *
+ * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+ * for which progress updates are being requested.
+ * @param listener The listener object to use.
+ */
+ public int addProgressListener(DownloadRequest downloadRequest,
+ DownloadProgressListener listener) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the listener AIDL from the API.
+ * @hide
+ */
+ @Override
+ public final int addProgressListener(final DownloadRequest downloadRequest,
+ final IDownloadProgressListener listener) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ if (downloadRequest == null) {
+ throw new NullPointerException("Download request must not be null");
+ }
+ if (listener == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ DownloadProgressListener exposedCallback = new VendorDownloadProgressListener(listener) {
+ @Override
+ protected void onRemoteException(RemoteException e) {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ }
+ };
+
+ int result = addProgressListener(downloadRequest, exposedCallback);
+
+ if (result == MbmsErrors.SUCCESS) {
+ DeathRecipient deathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+ mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+ }
+ };
+ mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+ listener.asBinder().linkToDeath(deathRecipient, 0);
+ mDownloadProgressListenerBinderMap.put(listener.asBinder(), exposedCallback);
+ }
+
+ return result;
+ }
+
+ /**
+ * Un-registers a download progress listener for the provided {@link DownloadRequest}.
+ *
+ * This method is called by the app when it no longer wants to request progress updates on the
+ * download.
+ *
+ * If the middleware is not aware of a download having been requested with the provided
+ * {@link DownloadRequest} in the past,
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+ * must be returned.
+ *
+ * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+ * @param listener The callback object that
+ * {@link #addProgressListener(DownloadRequest, DownloadProgressListener)}
+ * was called with.
+ */
+ public int removeProgressListener(DownloadRequest downloadRequest,
+ DownloadProgressListener listener) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the listener AIDL from the API.
+ * @hide
+ */
+ public final int removeProgressListener(
+ final DownloadRequest downloadRequest, final IDownloadProgressListener listener)
+ throws RemoteException {
+ if (downloadRequest == null) {
+ throw new NullPointerException("Download request must not be null");
+ }
+ if (listener == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ DeathRecipient deathRecipient =
+ mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+ if (deathRecipient == null) {
+ throw new IllegalArgumentException("Unknown listener");
+ }
+
+ listener.asBinder().unlinkToDeath(deathRecipient, 0);
+
+ DownloadProgressListener exposedCallback =
+ mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+ if (exposedCallback == null) {
+ throw new IllegalArgumentException("Unknown listener");
+ }
+
+ return removeProgressListener(downloadRequest, exposedCallback);
}
/**
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index db177c0..5ce612d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -77,6 +77,10 @@
@Override
public void onError(final int errorCode, final String message) {
try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
callback.onError(errorCode, message);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
@@ -173,6 +177,10 @@
@Override
public void onError(final int errorCode, final String message) {
try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
callback.onError(errorCode, message);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 303a068..93964f3 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -68,11 +68,6 @@
String getSubscriberIdForSubscriber(int subId, String callingPackage);
/**
- * Retrieves the Group Identifier Level1 for GSM phones.
- */
- String getGroupIdLevel1(String callingPackage);
-
- /**
* Retrieves the Group Identifier Level1 for GSM phones of a subId.
*/
String getGroupIdLevel1ForSubscriber(int subId, String callingPackage);
@@ -152,6 +147,13 @@
in ImsiEncryptionInfo imsiEncryptionInfo);
/**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ */
+ void resetCarrierKeysForImsiEncryption(int subId, String callingPackage);
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1ace032..9b71b8a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1481,4 +1481,17 @@
* screen is off) we want to turn on those indications even when the screen is off.
*/
void setRadioIndicationUpdateMode(int subId, int filters, int mode);
+
+ /**
+ * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+ * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
+ * (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
+ */
+ void setCarrierTestOverride(int subId, String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String plmn, String spn);
+
+ /**
+ * A test API to return installed carrier id list version.
+ */
+ int getCarrierIdListVersion(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ee7084a..d25fd3f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -419,6 +419,8 @@
int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
int RIL_REQUEST_START_KEEPALIVE = 146;
int RIL_REQUEST_STOP_KEEPALIVE = 147;
+ int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
+ int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f29d993c..51369d0 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -486,4 +486,10 @@
*/
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
+
+ /**
+ * Broadcast action to trigger the Carrier Certificate download.
+ */
+ public static final String ACTION_CARRIER_CERTIFICATE_DOWNLOAD =
+ "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD";
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 54e07a16..8fb6fda 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -40,3 +40,49 @@
// Pin java_version until jarjar is certified to support later versions. http://b/72703434
java_version: "1.8",
}
+
+doc_defaults {
+ name:"android.test.mock.docs-defaults",
+ srcs: ["src/android/test/mock/**/*.java"],
+
+ // Includes the main framework source to ensure that doclava has access to the
+ // visibility information for the base classes of the mock classes. Without it
+ // otherwise hidden methods could be visible.
+ srcs_lib: "framework",
+ srcs_lib_whitelist_dirs: ["core/java"],
+ srcs_lib_whitelist_pkgs: ["android"],
+ libs: [
+ "core-oj",
+ "core-libart",
+ "framework",
+ "conscrypt",
+ "okhttp",
+ "bouncycastle",
+ "ext",
+ ],
+ local_sourcepaths: ["src/android/test/mock"],
+ custom_template: "droiddoc-templates-sdk",
+ installable: false,
+}
+
+android_test_mock_docs_args =
+ "-hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 " +
+ "-stubpackages android.test.mock " +
+ "-nodocs "
+
+droiddoc {
+ name: "android.test.mock.docs",
+ defaults: ["android.test.mock.docs-defaults"],
+
+ api_tag_name: "ANDROID_TEST_MOCK",
+ api_filename: "api/android-test-mock-current.txt",
+ removed_api_filename: "api/android-test-mock-removed.txt",
+
+ args: android_test_mock_docs_args,
+}
+
+java_library_static {
+ name: "android.test.mock.stubs",
+ srcs: [":android.test.mock.docs"],
+ sdk_version: "current",
+}
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index 15fa12d1..08443c9 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -16,85 +16,30 @@
LOCAL_PATH:= $(call my-dir)
-# Includes the main framework source to ensure that doclava has access to the
-# visibility information for the base classes of the mock classes. Without it
-# otherwise hidden methods could be visible.
-android_test_mock_source_files := \
- $(call all-java-files-under, src/android/test/mock) \
- $(call all-java-files-under, ../core/java/android)
-
-# Generate the stub source files for android.test.mock.stubs
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework conscrypt okhttp bouncycastle
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
-
-ANDROID_TEST_MOCK_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/api.txt
-ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/removed.txt
-
-ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
-ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
-
-LOCAL_DROIDDOC_OPTIONS:= \
- -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
- -stubpackages android.test.mock \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
- -nodocs \
- -api $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) \
- -removedApi $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) \
-
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_MODULE := android-test-mock-api-stubs-gen
-
-include $(BUILD_DROIDDOC)
-
-# Remember the target that will trigger the code generation.
-android_test_mock_gen_stamp := $(full_target)
-
-# Add some additional dependencies
-$(ANDROID_TEST_MOCK_OUTPUT_API_FILE): $(full_target)
-$(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE): $(full_target)
-
-# Build the android.test.mock.stubs library
-# =========================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.stubs
-
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-
-# Make sure to run droiddoc first to generate the stub source files.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
-android_test_mock_gen_stamp :=
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Archive a copy of the classes.jar in SDK build.
+full_classes_jar := $(call intermediates-dir-for,JAVA_LIBRARIES,android.test.mock.stubs,,COMMON)/classes.jar
$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs.jar)
# Check that the android.test.mock.stubs library has not changed
# ==============================================================
+ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
+ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
# Check that the API we're building hasn't changed from the not-yet-released
# SDK version.
$(eval $(call check-api, \
check-android-test-mock-api-current, \
$(ANDROID_TEST_MOCK_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_API_FILE), \
+ $(INTERNAL_PLATFORM_ANDROID_TEST_MOCK_API_FILE), \
$(ANDROID_TEST_MOCK_REMOVED_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE), \
+ $(INTERNAL_PLATFORM_ANDROID_TEST_MOCK_REMOVED_API_FILE), \
-error 2 -error 3 -error 4 -error 5 -error 6 \
-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
-error 25 -error 26 -error 27, \
cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock.txt, \
check-android-test-mock-api, \
- $(call doc-timestamp-for,android-test-mock-api-stubs-gen) \
+ $(OUT_DOCS)/android.test.mock.docs-stubs.srcjar \
))
.PHONY: check-android-test-mock-api
@@ -103,8 +48,8 @@
.PHONY: update-android-test-mock-api
update-api: update-android-test-mock-api
-update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
+update-android-test-mock-api: $(INTERNAL_PLATFORM_ANDROID_TEST_MOCK_API_FILE) | $(ACP)
@echo Copying current.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
+ $(hide) $(ACP) $(INTERNAL_PLATFORM_ANDROID_TEST_MOCK_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
@echo Copying removed.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
+ $(hide) $(ACP) $(INTERNAL_PLATFORM_ANDROID_TEST_MOCK_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index a2edb04..9e95369 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -31,7 +31,7 @@
libs: [
"framework",
"android.test.base",
- "android.test.mock",
+ "android.test.mock.stubs",
],
}
@@ -48,7 +48,7 @@
libs: [
"framework",
"android.test.base",
- "android.test.mock",
+ "android.test.mock.stubs",
"junit",
],
}
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 6bdfdc6..85e8361 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -22,8 +22,12 @@
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+
+import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
+import java.util.Map.Entry;
import java.util.Random;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,19 +44,29 @@
};
@Test
- public void testDefaultTruncLen() throws Exception {
- IpSecAlgorithm explicit =
+ public void testNoTruncLen() throws Exception {
+ Entry<String, Integer>[] authAndAeadList =
+ new Entry[] {
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128),
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160),
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+ };
+
+ // Expect auth and aead algorithms to throw errors if trunclen is omitted.
+ for (Entry<String, Integer> algData : authAndAeadList) {
+ try {
new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256);
- IpSecAlgorithm implicit =
- new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
- assertTrue(
- "Default Truncation Length Incorrect, Explicit: "
- + explicit
- + "implicit: "
- + implicit,
- IpSecAlgorithm.equals(explicit, implicit));
+ algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8));
+ fail("Expected exception on unprovided auth trunclen");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ // Ensure crypt works with no truncation length supplied.
+ new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
}
@Test
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f186ee5..771faaf 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -62,7 +62,8 @@
c.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_MD5,
- new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+ new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0},
+ 128));
c.setAuthenticatedEncryption(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0ca20de..9191bd3 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -37,6 +37,7 @@
import com.android.server.IpSecService;
import java.net.InetAddress;
+import java.net.Socket;
import java.net.UnknownHostException;
import org.junit.Before;
@@ -179,7 +180,7 @@
IpSecManager.UdpEncapsulationSocket encapSocket =
mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
- assertNotNull(encapSocket.getSocket());
+ assertNotNull(encapSocket.getFileDescriptor());
assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
encapSocket.close();
@@ -188,6 +189,33 @@
}
@Test
+ public void testApplyTransportModeTransformEnsuresSocketCreation() throws Exception {
+ Socket socket = new Socket();
+ IpSecConfig dummyConfig = new IpSecConfig();
+ IpSecTransform dummyTransform = new IpSecTransform(null, dummyConfig);
+
+ // Even if underlying SocketImpl is not initalized, this should force the init, and
+ // thereby succeed.
+ mIpSecManager.applyTransportModeTransform(
+ socket, IpSecManager.DIRECTION_IN, dummyTransform);
+
+ // Check to make sure the FileDescriptor is non-null
+ assertNotNull(socket.getFileDescriptor$());
+ }
+
+ @Test
+ public void testRemoveTransportModeTransformsForcesSocketCreation() throws Exception {
+ Socket socket = new Socket();
+
+ // Even if underlying SocketImpl is not initalized, this should force the init, and
+ // thereby succeed.
+ mIpSecManager.removeTransportModeTransforms(socket);
+
+ // Check to make sure the FileDescriptor is non-null
+ assertNotNull(socket.getFileDescriptor$());
+ }
+
+ @Test
public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
IpSecUdpEncapResponse udpEncapResp =
new IpSecUdpEncapResponse(
@@ -202,7 +230,7 @@
IpSecManager.UdpEncapsulationSocket encapSocket =
mIpSecManager.openUdpEncapsulationSocket();
- assertNotNull(encapSocket.getSocket());
+ assertNotNull(encapSocket.getFileDescriptor());
assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
encapSocket.close();
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 69edc0c..0696592 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -273,14 +273,28 @@
@Test
public void testOemPaid() {
NetworkCapabilities nc = new NetworkCapabilities();
- nc.maybeMarkCapabilitiesRestricted();
+ // By default OEM_PAID is neither in the unwanted or required lists and the network is not
+ // restricted.
+ assertFalse(nc.hasUnwantedCapability(NET_CAPABILITY_OEM_PAID));
assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
+ nc.maybeMarkCapabilitiesRestricted();
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ // Adding OEM_PAID to capability list should make network restricted.
nc.addCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability.
nc.maybeMarkCapabilitiesRestricted();
assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // Now let's make request for OEM_PAID network.
+ NetworkCapabilities nr = new NetworkCapabilities();
+ nr.addCapability(NET_CAPABILITY_OEM_PAID);
+ nr.maybeMarkCapabilitiesRestricted();
+ assertTrue(nr.satisfiedByNetworkCapabilities(nc));
+
+ // Request fails for network with the default capabilities.
+ assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities()));
}
@Test
@@ -295,7 +309,8 @@
request.addUnwantedCapability(NET_CAPABILITY_WIFI_P2P);
request.addUnwantedCapability(NET_CAPABILITY_NOT_METERED);
assertTrue(request.satisfiedByNetworkCapabilities(network));
- assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P, NET_CAPABILITY_NOT_METERED},
+ assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P,
+ NET_CAPABILITY_NOT_METERED},
request.getUnwantedCapabilities());
// This is a default capability, just want to make sure its there because we use it below.
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 0530a86..8f18d07 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -39,8 +39,10 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Process;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
+import android.util.ArrayMap;
import com.google.android.collect.Sets;
@@ -773,6 +775,88 @@
assertEquals(entry2, stats.getValues(1, null));
}
+ @Test
+ public void testApply464xlatAdjustments() {
+ final String v4Iface = "v4-wlan0";
+ final String baseIface = "wlan0";
+ final String otherIface = "other";
+ final int appUid = 10001;
+ final int rootUid = Process.ROOT_UID;
+ ArrayMap<String, String> stackedIface = new ArrayMap<>();
+ stackedIface.put(v4Iface, baseIface);
+
+ NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+ otherIface, appUid, SET_DEFAULT, TAG_NONE,
+ 2600 /* rxBytes */,
+ 2 /* rxPackets */,
+ 3800 /* txBytes */,
+ 3 /* txPackets */,
+ 0 /* operations */);
+
+ NetworkStats stats = new NetworkStats(TEST_START, 3)
+ .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+ 30501490 /* rxBytes */,
+ 22401 /* rxPackets */,
+ 876235 /* txBytes */,
+ 13805 /* txPackets */,
+ 0 /* operations */)
+ .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+ 31113087,
+ 22588,
+ 1169942,
+ 13902,
+ 0)
+ .addValues(otherEntry);
+
+ stats.apply464xlatAdjustments(stackedIface);
+
+ assertEquals(3, stats.size());
+ assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ 30949510,
+ 22401,
+ 1152335,
+ 13805,
+ 0);
+ assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ 163577,
+ 187,
+ 17607,
+ 97,
+ 0);
+ assertEquals(otherEntry, stats.getValues(2, null));
+ }
+
+ @Test
+ public void testApply464xlatAdjustments_noStackedIface() {
+ NetworkStats.Entry firstEntry = new NetworkStats.Entry(
+ "if1", 10002, SET_DEFAULT, TAG_NONE,
+ 2600 /* rxBytes */,
+ 2 /* rxPackets */,
+ 3800 /* txBytes */,
+ 3 /* txPackets */,
+ 0 /* operations */);
+ NetworkStats.Entry secondEntry = new NetworkStats.Entry(
+ "if2", 10002, SET_DEFAULT, TAG_NONE,
+ 5000 /* rxBytes */,
+ 3 /* rxPackets */,
+ 6000 /* txBytes */,
+ 4 /* txPackets */,
+ 0 /* operations */);
+
+ NetworkStats stats = new NetworkStats(TEST_START, 2)
+ .addValues(firstEntry)
+ .addValues(secondEntry);
+
+ // Empty map: no adjustment
+ stats.apply464xlatAdjustments(new ArrayMap<>());
+
+ assertEquals(2, stats.size());
+ assertEquals(firstEntry, stats.getValues(0, null));
+ assertEquals(secondEntry, stats.getValues(1, null));
+ }
+
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 9b75a50..9364ec8 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -16,6 +16,7 @@
package android.net.apf;
+import static android.net.util.NetworkConstants.*;
import static android.system.OsConstants.*;
import static com.android.internal.util.BitUtils.bytesToBEInt;
import static com.android.internal.util.BitUtils.put;
@@ -26,13 +27,14 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpManager;
+import android.net.ip.IpClient;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
@@ -82,6 +84,7 @@
private static final int TIMEOUT_MS = 500;
@Mock IpConnectivityLog mLog;
+ @Mock Context mContext;
@Before
public void setUp() throws Exception {
@@ -603,7 +606,7 @@
}
}
- private class MockIpManagerCallback extends IpManager.Callback {
+ private class MockIpClientCallback extends IpClient.Callback {
private final ConditionVariable mGotApfProgram = new ConditionVariable();
private byte[] mLastApfProgram;
@@ -633,9 +636,9 @@
private FileDescriptor mWriteSocket;
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
- public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
- IpConnectivityLog log) throws Exception {
- super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
+ public TestApfFilter(Context context, ApfConfiguration config,
+ IpClient.Callback ipClientCallback, IpConnectivityLog log) throws Exception {
+ super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
@@ -757,19 +760,30 @@
private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
+ // Helper to initialize a default apfFilter.
+ private ApfFilter setupApfFilter(IpClient.Callback ipClientCallback, ApfConfiguration config)
+ throws Exception {
+ LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(link);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ apfFilter.setLinkProperties(lp);
+ return apfFilter;
+ }
+
@Test
public void testApfFilterIPv4() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -816,10 +830,10 @@
@Test
public void testApfFilterIPv6() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- byte[] program = ipManagerCallback.getApfProgram();
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty IPv6 packet is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -854,17 +868,17 @@
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfConfiguration config = getDefaultConfig();
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
@@ -901,9 +915,9 @@
assertPass(program, bcastv4unicastl2packet.array());
// Turn on multicast filter and verify it works
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
@@ -911,9 +925,9 @@
assertDrop(program, bcastv4unicastl2packet.array());
// Turn off multicast filter and verify it's off
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertPass(program, mcastv4packet.array());
assertPass(program, mcastv6packet.array());
assertPass(program, bcastv4packet1.array());
@@ -921,13 +935,13 @@
assertPass(program, bcastv4unicastl2packet.array());
// Verify it can be initialized to on
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+ apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
@@ -941,17 +955,48 @@
}
@Test
+ public void testApfFilterMulticastPingWhileDozing() throws Exception {
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig());
+
+ // Construct a multicast ICMPv6 ECHO request.
+ final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+ packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE);
+ put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
+
+ // Normally, we let multicast pings alone...
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
+
+ // ...and even while dozing...
+ apfFilter.setDozeMode(true);
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
+
+ // ...but when the multicast filter is also enabled, drop the multicast pings to save power.
+ apfFilter.setMulticastFilter(true);
+ assertDrop(ipClientCallback.getApfProgram(), packet.array());
+
+ // However, we should still let through all other ICMPv6 types.
+ ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
+ raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
+ assertPass(ipClientCallback.getApfProgram(), raPacket.array());
+
+ // Now wake up from doze mode to ensure that we no longer drop the packets.
+ // (The multicast filter is still enabled at this point).
+ apfFilter.setDozeMode(false);
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
+
+ apfFilter.shutdown();
+ }
+
+ @Test
public void testApfFilter802_3() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipManagerCallback.getApfProgram();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
// Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -967,12 +1012,11 @@
assertPass(program, packet.array());
// Now turn on the filter
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- apfFilter.setLinkProperties(lp);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IEEE802.3 frame is dropped
// In this case ethtype is used for payload length
@@ -992,19 +1036,14 @@
@Test
public void testApfFilterEthTypeBL() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
final int[] emptyBlackList = {};
final int[] ipv4BlackList = {ETH_P_IP};
final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipManagerCallback.getApfProgram();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
// Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -1020,12 +1059,11 @@
assertPass(program, packet.array());
// Now add IPv4 to the black list
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ethTypeBlackList = ipv4BlackList;
- apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- apfFilter.setLinkProperties(lp);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IPv4 frame will be dropped
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1036,12 +1074,11 @@
assertPass(program, packet.array());
// Now let us have both IPv4 and IPv6 in the black list
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ethTypeBlackList = ipv4Ipv6BlackList;
- apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- apfFilter.setLinkProperties(lp);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IPv4 frame will be dropped
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1054,7 +1091,7 @@
apfFilter.shutdown();
}
- private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
+ private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) {
cb.resetApfProgramWait();
filter.setLinkProperties(lp);
return cb.getApfProgram();
@@ -1077,23 +1114,23 @@
@Test
public void testApfFilterArp() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
// Verify initially ARP request filter is off, and GARP filter is on.
- verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
+ verifyArpFilter(ipClientCallback.getApfProgram(), PASS);
// Inform ApfFilter of our address and verify ARP filtering is on
LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
LinkProperties lp = new LinkProperties();
assertTrue(lp.addLinkAddress(linkAddress));
- verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP);
+ verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP);
// Inform ApfFilter of loss of IP and verify ARP filtering is off
- verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS);
+ verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS);
apfFilter.shutdown();
}
@@ -1124,7 +1161,7 @@
return packet.array();
}
- // Verify that the last program pushed to the IpManager.Callback properly filters the
+ // Verify that the last program pushed to the IpClient.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
final int FRACTION_OF_LIFETIME = 6;
@@ -1154,12 +1191,12 @@
// Test that when ApfFilter is shown the given packet, it generates a program to filter it
// for the given lifetime.
- private void verifyRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+ private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
// Verify new program generated if ApfFilter witnesses RA
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.pretendPacketReceived(packet.array());
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
verifyRaLifetime(program, packet, lifetime);
}
@@ -1192,21 +1229,21 @@
&& (ev1.dnsslLifetime == ev2.dnsslLifetime);
}
- private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+ private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
ByteBuffer packet) throws IOException, ErrnoException {
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.pretendPacketReceived(packet.array());
- ipManagerCallback.assertNoProgramUpdate();
+ ipClientCallback.assertNoProgramUpdate();
}
@Test
public void testApfFilterRa() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
- byte[] program = ipManagerCallback.getApfProgram();
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ byte[] program = ipClientCallback.getApfProgram();
final int ROUTER_LIFETIME = 1000;
final int PREFIX_VALID_LIFETIME = 200;
@@ -1231,7 +1268,7 @@
basePacket.put(IPV6_ALL_NODES_ADDRESS);
assertPass(program, basePacket.array());
- verifyRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
@@ -1249,7 +1286,7 @@
zeroLengthOptionPacket.put(basePacket);
zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
zeroLengthOptionPacket.put((byte)0);
- assertInvalidRa(apfFilter, ipManagerCallback, zeroLengthOptionPacket);
+ assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket);
// Generate several RAs with different options and lifetimes, and verify when
// ApfFilter is shown these packets, it generates programs to filter them for the
@@ -1267,7 +1304,7 @@
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
PREFIX_VALID_LIFETIME);
verifyRaLifetime(
- apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
+ apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
verifyRaEvent(new RaEvent(
ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
@@ -1279,7 +1316,7 @@
rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
rdnssOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
@@ -1290,7 +1327,7 @@
routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
routeInfoOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
@@ -1301,11 +1338,11 @@
dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
dnsslOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
// Verify that current program filters all five RAs:
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
@@ -1347,11 +1384,11 @@
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- MockIpManagerCallback cb = new MockIpManagerCallback();
+ MockIpClientCallback cb = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
@@ -1368,11 +1405,11 @@
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- MockIpManagerCallback cb = new MockIpManagerCallback();
+ MockIpClientCallback cb = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java
new file mode 100644
index 0000000..8012838
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceSetTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.net.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceSetTest {
+ @Test
+ public void testNullNamesIgnored() {
+ final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
+ assertEquals(2, set.ifnames.size());
+ assertTrue(set.ifnames.contains("if1"));
+ assertTrue(set.ifnames.contains("if2"));
+ }
+
+ @Test
+ public void testToString() {
+ final InterfaceSet set = new InterfaceSet("if1", "if2");
+ final String setString = set.toString();
+ assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
+ }
+
+ @Test
+ public void testToString_Empty() {
+ final InterfaceSet set = new InterfaceSet(null, null);
+ assertEquals("[]", set.toString());
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
+ assertEquals(new InterfaceSet(null, null), new InterfaceSet());
+ assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
+ assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
+ assertFalse(new InterfaceSet().equals(null));
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 82b7bec..e4df974 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -17,6 +17,9 @@
package com.android.server;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -70,6 +73,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -157,6 +161,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -181,6 +186,9 @@
private static final int TIMEOUT_MS = 500;
private static final int TEST_LINGER_DELAY_MS = 120;
+ private static final String MOBILE_IFNAME = "test_rmnet_data0";
+ private static final String WIFI_IFNAME = "test_wlan0";
+
private MockContext mServiceContext;
private WrappedConnectivityService mService;
private WrappedConnectivityManager mCm;
@@ -751,7 +759,7 @@
// NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
private class WrappedNetworkMonitor extends NetworkMonitor {
- public Handler connectivityHandler;
+ public final Handler connectivityHandler;
// HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
public int gen204ProbeResult = 500;
public String gen204ProbeRedirectUrl = null;
@@ -872,6 +880,10 @@
return mMetricsService;
}
+ @Override
+ protected void registerNetdEventCallback() {
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -926,6 +938,7 @@
// Ensure that the default setting for Captive Portals is used for most tests
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
setMobileDataAlwaysOn(false);
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
@After
@@ -2580,6 +2593,14 @@
waitForIdle();
}
+ private void setPrivateDnsSettings(String mode, String specifier) {
+ final ContentResolver cr = mServiceContext.getContentResolver();
+ Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_MODE, mode);
+ Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_SPECIFIER, specifier);
+ mService.updatePrivateDnsSettings();
+ waitForIdle();
+ }
+
private boolean isForegroundNetwork(MockNetworkAgent network) {
NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
assertNotNull(nc);
@@ -3579,7 +3600,7 @@
mCm.registerNetworkCallback(networkRequest, networkCallback);
LinkProperties lp = new LinkProperties();
- lp.setInterfaceName("wlan0");
+ lp.setInterfaceName(WIFI_IFNAME);
LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24");
RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null,
NetworkUtils.numericToInetAddress("192.168.12.1"), lp.getInterfaceName());
@@ -3668,52 +3689,63 @@
@Test
public void testBasicDnsConfigurationPushed() throws Exception {
- final String IFNAME = "test_rmnet_data0";
- final String[] EMPTY_TLS_SERVERS = new String[0];
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+ ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
+
+ // Clear any interactions that occur as a result of CS starting up.
+ reset(mNetworkManagementService);
+
+ final String[] EMPTY_STRING_ARRAY = new String[0];
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
waitForIdle();
verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
- anyInt(), any(), any(), any(), anyString(), eq(EMPTY_TLS_SERVERS));
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
+ verifyNoMoreInteractions(mNetworkManagementService);
final LinkProperties cellLp = new LinkProperties();
- cellLp.setInterfaceName(IFNAME);
+ cellLp.setInterfaceName(MOBILE_IFNAME);
// Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
// "is-reachable" testing in order to not program netd with unreachable
// nameservers that it might try repeated to validate.
cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
- cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), IFNAME));
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+ MOBILE_IFNAME));
cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
- cellLp.addRoute(
- new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), IFNAME));
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
+ MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(false);
waitForIdle();
- verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
- anyInt(), mStringArrayCaptor.capture(), any(), any(),
- anyString(), eq(EMPTY_TLS_SERVERS));
// CS tells netd about the empty DNS config for this network.
- assertEmpty(mStringArrayCaptor.getValue());
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
reset(mNetworkManagementService);
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- anyString(), eq(EMPTY_TLS_SERVERS));
+ eq(""), tlsServers.capture());
assertEquals(1, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
+ // Opportunistic mode.
+ assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1"));
reset(mNetworkManagementService);
cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- anyString(), eq(EMPTY_TLS_SERVERS));
+ eq(""), tlsServers.capture());
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
+ // Opportunistic mode.
+ assertEquals(2, tlsServers.getValue().length);
+ assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
reset(mNetworkManagementService);
final String TLS_SPECIFIER = "tls.example.com";
@@ -3726,7 +3758,7 @@
mCellNetworkAgent.getNetwork().netId,
new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
waitForIdle();
- verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
eq(TLS_SPECIFIER), eq(TLS_SERVERS));
assertEquals(2, mStringArrayCaptor.getValue().length);
@@ -3735,6 +3767,195 @@
reset(mNetworkManagementService);
}
+ @Test
+ public void testPrivateDnsSettingsChange() throws Exception {
+ final String[] EMPTY_STRING_ARRAY = new String[0];
+ ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
+
+ // Clear any interactions that occur as a result of CS starting up.
+ reset(mNetworkManagementService);
+
+ // The default on Android is opportunistic mode ("Automatic").
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ waitForIdle();
+ // CS tells netd about the empty DNS config for this network.
+ verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
+ verifyNoMoreInteractions(mNetworkManagementService);
+
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
+ // "is-reachable" testing in order to not program netd with unreachable
+ // nameservers that it might try repeated to validate.
+ cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+ MOBILE_IFNAME));
+ cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
+ MOBILE_IFNAME));
+ cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
+ cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
+
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(),
+ eq(""), tlsServers.capture());
+ assertEquals(2, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ // Opportunistic mode.
+ assertEquals(2, tlsServers.getValue().length);
+ assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ reset(mNetworkManagementService);
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ mCellNetworkAgent);
+ CallbackInfo cbi = cellNetworkCallback.expectCallback(
+ CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(),
+ eq(""), eq(EMPTY_STRING_ARRAY));
+ assertEquals(2, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ reset(mNetworkManagementService);
+ cellNetworkCallback.assertNoCallback();
+
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+ verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(),
+ eq(""), tlsServers.capture());
+ assertEquals(2, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ assertEquals(2, tlsServers.getValue().length);
+ assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ reset(mNetworkManagementService);
+ cellNetworkCallback.assertNoCallback();
+
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
+ // Can't test dns configuration for strict mode without properly mocking
+ // out the DNS lookups, but can test that LinkProperties is updated.
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ }
+
+ @Test
+ public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception {
+ // The default on Android is opportunistic mode ("Automatic").
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ waitForIdle();
+ LinkProperties lp = new LinkProperties();
+ mCellNetworkAgent.sendLinkProperties(lp);
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ mCellNetworkAgent);
+ CallbackInfo cbi = cellNetworkCallback.expectCallback(
+ CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ Set<InetAddress> dnsServers = new HashSet<>();
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // Send a validation event for a server that is not part of the current
+ // resolver config. The validation event should be ignored.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "", "145.100.185.18", true);
+ cellNetworkCallback.assertNoCallback();
+
+ // Add a dns server to the LinkProperties.
+ LinkProperties lp2 = new LinkProperties(lp);
+ lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
+ mCellNetworkAgent.sendLinkProperties(lp2);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ dnsServers.add(InetAddress.getByName("145.100.185.16"));
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // Send a validation event containing a hostname that is not part of
+ // the current resolver config. The validation event should be ignored.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "hostname", true);
+ cellNetworkCallback.assertNoCallback();
+
+ // Send a validation event where validation failed.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", false);
+ cellNetworkCallback.assertNoCallback();
+
+ // Send a validation event where validation succeeded for a server in
+ // the current resolver config. A LinkProperties callback with updated
+ // private dns fields should be sent.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // The private dns fields in LinkProperties should be preserved when
+ // the network agent sends unrelated changes.
+ LinkProperties lp3 = new LinkProperties(lp2);
+ lp3.setMtu(1300);
+ mCellNetworkAgent.sendLinkProperties(lp3);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ checkDnsServers(cbi.arg, dnsServers);
+ assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+
+ // Removing the only validated server should affect the private dns
+ // fields in LinkProperties.
+ LinkProperties lp4 = new LinkProperties(lp3);
+ lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
+ mCellNetworkAgent.sendLinkProperties(lp4);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ dnsServers.remove(InetAddress.getByName("145.100.185.16"));
+ checkDnsServers(cbi.arg, dnsServers);
+ assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+ }
+
private void checkDirectlyConnectedRoutes(Object callbackObj,
Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
assertTrue(callbackObj instanceof LinkProperties);
@@ -3752,6 +3973,13 @@
assertTrue(observedRoutes.containsAll(expectedRoutes));
}
+ private static void checkDnsServers(Object callbackObj, Set<InetAddress> dnsServers) {
+ assertTrue(callbackObj instanceof LinkProperties);
+ LinkProperties lp = (LinkProperties) callbackObj;
+ assertEquals(dnsServers.size(), lp.getDnsServers().size());
+ assertTrue(lp.getDnsServers().containsAll(dnsServers));
+ }
+
private static <T> void assertEmpty(T[] ts) {
int length = ts.length;
assertEquals("expected empty array, but length was " + length, 0, length);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
new file mode 100644
index 0000000..bcd8bf3
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018, 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.server.connectivity;
+
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.os.INetworkManagementService;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import java.net.InetAddress;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DnsManager}.
+ *
+ * Build, install and run with:
+ * runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsManagerTest {
+ static final int TEST_NETID = 100;
+ static final int TEST_NETID_ALTERNATE = 101;
+ static final int TEST_NETID_UNTRACKED = 102;
+ final boolean IS_DEFAULT = true;
+ final boolean NOT_DEFAULT = false;
+
+ DnsManager mDnsManager;
+ MockContentResolver mContentResolver;
+
+ @Mock Context mCtx;
+ @Mock INetworkManagementService mNMService;
+ @Mock MockableSystemProperties mSystemProperties;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY,
+ new FakeSettingsProvider());
+ when(mCtx.getContentResolver()).thenReturn(mContentResolver);
+ mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
+
+ // Clear the private DNS settings
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE, "");
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, "");
+ }
+
+ @Test
+ public void testTrackedValidationUpdates() throws Exception {
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID_ALTERNATE),
+ mDnsManager.getPrivateDnsConfig());
+ LinkProperties lp = new LinkProperties();
+ lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+ lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
+
+ // Send a validation event that is tracked on the alternate netId
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
+ InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ LinkProperties fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+ assertFalse(fixedLp.isPrivateDnsActive());
+ assertNull(fixedLp.getPrivateDnsServerName());
+ fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID_ALTERNATE, fixedLp);
+ assertTrue(fixedLp.isPrivateDnsActive());
+ assertNull(fixedLp.getPrivateDnsServerName());
+
+ // Switch to strict mode
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, "strictmode.com");
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+ assertTrue(fixedLp.isPrivateDnsActive());
+ assertEquals("strictmode.com", fixedLp.getPrivateDnsServerName());
+ fixedLp = new LinkProperties(lp);
+ }
+
+ @Test
+ public void testIgnoreUntrackedValidationUpdates() throws Exception {
+ // The PrivateDnsConfig map is empty, so no validation events will
+ // be tracked.
+ LinkProperties lp = new LinkProperties();
+ lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked netId
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked ipAddress
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked hostname
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "hostname",
+ true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event failed
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", false));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Network removed
+ mDnsManager.removeNetwork(new Network(TEST_NETID));
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Turn private DNS mode off
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd4..d241b32 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -19,16 +19,25 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -36,6 +45,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -45,18 +55,28 @@
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
+import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
-import android.net.NetworkRequest;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -74,10 +94,16 @@
import android.telephony.CarrierConfigManager;
import android.test.mock.MockContentResolver;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
+import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import org.junit.After;
import org.junit.Before;
@@ -86,33 +112,45 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetheringTest {
+ private static final int IFINDEX_OFFSET = 100;
+
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+ private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+ private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
+ private static final String TEST_USB_IFNAME = "test_rndis0";
+ private static final String TEST_WLAN_IFNAME = "test_wlan0";
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- @Mock private ConnectivityManager mConnectivityManager;
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
@Mock private INetworkPolicyManager mPolicyManager;
@Mock private MockableSystemProperties mSystemProperties;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
- @Mock private TetheringDependencies mTetheringDependencies;
@Mock private UsbManager mUsbManager;
@Mock private WifiManager mWifiManager;
@Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+ @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+ @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private INetd mNetd;
+
+ private final MockTetheringDependencies mTetheringDependencies =
+ new MockTetheringDependencies();
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
- private final String mTestIfname = "test_wlan0";
private Vector<Intent> mIntents;
private BroadcastInterceptingContext mServiceContext;
@@ -139,30 +177,141 @@
@Override
public Object getSystemService(String name) {
- if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
if (Context.USB_SERVICE.equals(name)) return mUsbManager;
return super.getSystemService(name);
}
}
+ public class MockTetheringDependencies extends TetheringDependencies {
+ StateMachine upstreamNetworkMonitorMasterSM;
+ ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+ int isTetheringSupportedCalls;
+
+ public void reset() {
+ upstreamNetworkMonitorMasterSM = null;
+ ipv6CoordinatorNotifyList = null;
+ isTetheringSupportedCalls = 0;
+ }
+
+ @Override
+ public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+ return mOffloadHardwareInterface;
+ }
+
+ @Override
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
+ StateMachine target, SharedLog log, int what) {
+ upstreamNetworkMonitorMasterSM = target;
+ return mUpstreamNetworkMonitor;
+ }
+
+ @Override
+ public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ipv6CoordinatorNotifyList = notifyList;
+ return mIPv6TetheringCoordinator;
+ }
+
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
+
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
+ TEST_MOBILE_IFNAME };
+ final int index = ArrayUtils.indexOf(ifaces, ifName);
+ assertTrue("Non-mocked interface: " + ifName, index >= 0);
+ return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
+
+ @Override
+ public boolean isTetheringSupported() {
+ isTetheringSupportedCalls++;
+ return true;
+ }
+ }
+
+ private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
+ boolean with464xlat) {
+ final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null);
+ info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_MOBILE_IFNAME);
+
+ if (withIPv4) {
+ prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+ }
+
+ if (withIPv6) {
+ prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+ prop.addLinkAddress(
+ new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+ NetworkConstants.RFC7421_PREFIX_LENGTH));
+ prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+ }
+
+ if (with464xlat) {
+ final LinkProperties stackedLink = new LinkProperties();
+ stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
+ stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+
+ prop.addStackedLink(stackedLink);
+ }
+
+
+ final NetworkCapabilities capabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
+ return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
+ }
+
+ private static NetworkState buildMobileIPv4UpstreamState() {
+ return buildMobileUpstreamState(true, false, false);
+ }
+
+ private static NetworkState buildMobileIPv6UpstreamState() {
+ return buildMobileUpstreamState(false, true, false);
+ }
+
+ private static NetworkState buildMobileDualStackUpstreamState() {
+ return buildMobileUpstreamState(true, true, false);
+ }
+
+ private static NetworkState buildMobile464xlatUpstreamState() {
+ return buildMobileUpstreamState(false, true, true);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[] { "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
+ .thenReturn(new String[]{ "test_wlan\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
when(mNMService.listInterfaces())
- .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
+ .thenReturn(new String[] {
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
when(mNMService.getInterfaceConfig(anyString()))
.thenReturn(new InterfaceConfiguration());
+ when(mRouterAdvertisementDaemon.start())
+ .thenReturn(true);
mServiceContext = new MockContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
@@ -175,9 +324,8 @@
}
};
mServiceContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
- when(mTetheringDependencies.getOffloadHardwareInterface(
- any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
+ new IntentFilter(ACTION_TETHER_STATE_CHANGED));
+ mTetheringDependencies.reset();
mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
mLooper.getLooper(), mSystemProperties,
mTetheringDependencies);
@@ -207,6 +355,7 @@
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
+ sendConfigurationChanged();
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -215,6 +364,7 @@
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
+ sendConfigurationChanged();
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// We therefore still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
@@ -224,6 +374,7 @@
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ sendConfigurationChanged();
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -263,17 +414,22 @@
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void sendConfigurationChanged() {
+ final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void verifyInterfaceServingModeStarted() throws Exception {
- verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
+ verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(1))
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(mTestIfname);
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
}
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
// Verify that ifname is in the whichExtra array of the tether state changed broadcast.
final Intent bcast = mIntents.get(0);
- assertEquals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, bcast.getAction());
+ assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction());
final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra);
assertTrue(ifnames.contains(ifname));
mIntents.remove(bcast);
@@ -281,13 +437,11 @@
public void failingLocalOnlyHotspotLegacyApBroadcast(
boolean emulateInterfaceStatusChanged) throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
-
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
@@ -296,30 +450,31 @@
// then it creates a TetherInterfaceStateMachine and sends out a
// broadcast indicating that the interface is "available".
if (emulateInterfaceStatusChanged) {
- verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
}
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
- @Test
- public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+ private void prepareUsbTethering(NetworkState upstreamState) {
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+ .thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(TETHERING_USB, null, false);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
- // Pretend we receive a USB connected broadcast. Here we also pretend
- // that the RNDIS function is somehow enabled, so that we see if we
- // might trip ourselves up.
- sendUsbBroadcast(true, false, true);
- mLooper.dispatchAll();
+ mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+ }
+
+ @Test
+ public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ prepareUsbTethering(upstreamState);
+
// This should produce no activity of any kind.
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
// Pretend we then receive USB configured broadcast.
@@ -328,6 +483,10 @@
// Now we should see the start of tethering mechanics (in this case:
// tetherMatchingInterfaces() which starts by fetching all interfaces).
verify(mNMService, times(1)).listInterfaces();
+
+ // UpstreamNetworkMonitor should receive selected upstream
+ verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
@Test
@@ -342,55 +501,158 @@
public void workingLocalOnlyHotspotEnrichedApBroadcast(
boolean emulateInterfaceStatusChanged) throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
-
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
- // UpstreamNetworkMonitor will be started, and will register two callbacks:
- // a "listen all" and a "track default".
- verify(mConnectivityManager, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
+ verify(mUpstreamNetworkMonitor, times(1)).start();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyNoMoreInteractions(mConnectivityManager);
+ assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(mTestIfname);
+ mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+ verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
- mTethering.getLastTetherError(mTestIfname));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+ }
+
+ /**
+ * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+ */
+ private void sendIPv6TetherUpdates(NetworkState upstreamState) {
+ // IPv6TetheringCoordinator must have been notified of downstream
+ verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
+ argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
+ eq(IControlsTethering.STATE_TETHERED));
+
+ for (TetherInterfaceStateMachine tism :
+ mTetheringDependencies.ipv6CoordinatorNotifyList) {
+ NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
+ tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+ upstreamState.linkProperties.isIPv6Provisioned()
+ ? ipv6OnlyState.linkProperties
+ : null);
+ }
+ mLooper.dispatchAll();
+ }
+
+ private void runUsbTethering(NetworkState upstreamState) {
+ prepareUsbTethering(upstreamState);
+ sendUsbBroadcast(true, true, true);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv4() throws Exception {
+ NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv6() throws Exception {
+ NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_DualStack() throws Exception {
+ NetworkState upstreamState = buildMobileDualStackUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRouterAdvertisementDaemon, times(1)).start();
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
+ NetworkState upstreamState = buildMobile464xlatUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+ TEST_XLAT_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
+ // Setup IPv6
+ NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+ // Then 464xlat comes up
+ upstreamState = buildMobile464xlatUpstreamState();
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+ .thenReturn(upstreamState);
+
+ // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
+ mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+ Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+ 0,
+ upstreamState);
+ mLooper.dispatchAll();
+
+ // Forwarding is added for 464xlat
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+ TEST_XLAT_MOBILE_IFNAME);
+ // Forwarding was not re-added for v6 (still times(1))
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
}
@Test
@@ -406,7 +668,6 @@
// TODO: Test with and without interfaceStatusChanged().
@Test
public void failingWifiTetheringLegacyApBroadcast() throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
@@ -414,19 +675,17 @@
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
- verifyNoMoreInteractions(mConnectivityManager);
+ assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
@@ -434,7 +693,6 @@
// TODO: Test with and without interfaceStatusChanged().
@Test
public void workingWifiTetheringEnrichedApBroadcast() throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
@@ -442,39 +700,30 @@
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
- // UpstreamNetworkMonitor will be started, and will register two callbacks:
- // a "listen all" and a "track default".
- verify(mConnectivityManager, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
+ verify(mUpstreamNetworkMonitor, times(1)).start();
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
- verify(mConnectivityManager, times(1)).requestNetwork(
- any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
- any(Handler.class));
+ verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyNoMoreInteractions(mConnectivityManager);
+ assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
/////
// We do not currently emulate any upstream being found.
@@ -488,34 +737,31 @@
mLooper.dispatchAll();
verify(mWifiManager, times(1)).stopSoftAp();
verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
// Emulate externally-visible WifiManager effects, when tethering mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(mTestIfname);
+ mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+ verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
- mTethering.getLastTetherError(mTestIfname));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
}
// TODO: Test with and without interfaceStatusChanged().
@Test
public void failureEnablingIpForwarding() throws Exception {
- when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true);
@@ -524,27 +770,27 @@
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
// We verify get/set called twice here: once for setup and once during
// teardown because all events happen over the course of the single
// dispatchAll() above.
- verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(2))
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(mTestIfname);
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
- verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
+ // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
+ assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
// This never gets called because of the exception thrown above.
@@ -552,12 +798,11 @@
// When the master state machine transitions to an error state it tells
// downstream interfaces, which causes us to tell Wi-Fi about the error
// so it can take down AP mode.
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
}
@@ -596,7 +841,7 @@
@Test
public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = false;
final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 1;
@@ -618,7 +863,7 @@
@Test
public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = true;
final boolean nextDisallow = false;
final int expectedInteractionsWithShowNotification = 0;
@@ -629,7 +874,7 @@
@Test
public void testDisallowTetheringUnchanged() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final int expectedInteractionsWithShowNotification = 0;
boolean currDisallow = true;
boolean nextDisallow = true;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f59850d..e377a47 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -70,6 +70,7 @@
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.test.filters.SmallTest;
@@ -88,6 +89,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -639,4 +642,32 @@
lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
assertTrue(Vpn.providesRoutesToMostDestinations(lp));
}
+
+ @Test
+ public void testDoesNotLockUpWithTooManyRoutes() {
+ final LinkProperties lp = new LinkProperties();
+ final byte[] ad = new byte[4];
+ // Actually evaluating this many routes under 1500ms is impossible on
+ // current hardware and for some time, as the algorithm is O(n²).
+ // Make sure the system has a safeguard against this and does not
+ // lock up.
+ final int MAX_ROUTES = 4000;
+ final long MAX_ALLOWED_TIME_MS = 1500;
+ for (int i = 0; i < MAX_ROUTES; ++i) {
+ ad[0] = (byte)((i >> 24) & 0xFF);
+ ad[1] = (byte)((i >> 16) & 0xFF);
+ ad[2] = (byte)((i >> 8) & 0xFF);
+ ad[3] = (byte)(i & 0xFF);
+ try {
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32)));
+ } catch (UnknownHostException e) {
+ // UnknownHostException is only thrown for an address of illegal length,
+ // which can't happen in the case above.
+ }
+ }
+ final long start = SystemClock.currentThreadTimeMillis();
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+ final long end = SystemClock.currentThreadTimeMillis();
+ assertTrue(end - start < MAX_ALLOWED_TIME_MS);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..7c77cf5 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -31,7 +31,6 @@
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
@@ -39,12 +38,12 @@
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -75,6 +74,7 @@
@Mock private IControlsTethering mTetherHelper;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
+ @Mock private TetheringDependencies mTetheringDependencies;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -84,7 +84,7 @@
private void initStateMachine(int interfaceType) throws Exception {
mTestedSm = new TetherInterfaceStateMachine(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
- mNMService, mStatsService, mTetherHelper);
+ mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
@@ -111,7 +111,8 @@
@Test
public void startsOutAvailable() {
mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
- TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper);
+ TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
+ mTetheringDependencies);
mTestedSm.start();
mLooper.dispatchAll();
verify(mTetherHelper).updateInterfaceState(
@@ -346,7 +347,7 @@
* Send a command to the state machine under test, and run the event loop to idle.
*
* @param command One of the TetherInterfaceStateMachine.CMD_* constants.
- * @param obj An additional argument to pass.
+ * @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
mTestedSm.sendMessage(command, arg1);
@@ -371,7 +372,7 @@
*/
private void dispatchTetherConnectionChanged(String upstreamIface) {
mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- upstreamIface);
+ new InterfaceSet(upstreamIface));
mLooper.dispatchAll();
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c3b9def..9661dc2 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -147,6 +147,16 @@
}
@Test
+ public void testCallbacksRegistered() {
+ mUNM.start();
+ verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
+ verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+
+ mUNM.stop();
+ verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+ }
+
+ @Test
public void testRequestsMobileNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index d415d22..152e6c3 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -34,6 +34,8 @@
env->GetArrayLength(program),
(uint8_t*)env->GetByteArrayElements(packet, NULL),
env->GetArrayLength(packet),
+ nullptr,
+ 0,
filter_age);
}
@@ -143,7 +145,8 @@
do {
apf_packet = pcap_next(apf_pcap.get(), &apf_header);
} while (apf_packet != NULL && !accept_packet(
- apf_program, apf_program_len, apf_packet, apf_header.len, 0));
+ apf_program, apf_program_len, apf_packet, apf_header.len,
+ nullptr, 0, 0));
// Make sure both filters matched the same packet.
if (apf_packet == NULL && bpf_packet == NULL)
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
index 9223931..a502a9d 100644
--- a/tools/bit/util.cpp
+++ b/tools/bit/util.cpp
@@ -241,6 +241,8 @@
char* buf = (char*)malloc(size);
if ((size_t) size != fread(buf, 1, size, file)) {
+ free(buf);
+ fclose(file);
return string();
}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 8804c8a..3d535f0 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -34,10 +34,6 @@
# This only works if the class name matches the file name and the directory structure
# matches the package.
local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
-# Utility variables to allow replacing a space with a comma
-comma:= ,
-empty:=
-space:= $(empty) $(empty)
# Convert class name list to jacoco exclude list
# This appends a * to all classes and replace the space separators with commas.
# These patterns will match all classes in this module and their inner classes.