Merge "Clean new TelephonyManager API."
diff --git a/Android.bp b/Android.bp
index baf8e4f..80df8c5 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",
diff --git a/api/current.txt b/api/current.txt
index 308afda..4c6e621 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37037,10 +37037,8 @@
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.SecureElementListener {
+ method public abstract void onServiceConnected();
}
public class Session {
@@ -37050,9 +37048,7 @@
method public android.se.omapi.Reader getReader();
method public boolean isClosed();
method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
- method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
- method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
}
}
@@ -40396,6 +40392,8 @@
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
+ method public int addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener);
+ method public int addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener);
method public int 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);
@@ -40403,12 +40401,12 @@
method public int 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 int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener);
+ method public int 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";
@@ -41063,21 +41061,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);
@@ -41086,46 +41084,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);
}
@@ -41261,6 +41258,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();
@@ -41284,15 +41286,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 {
diff --git a/api/system-current.txt b/api/system-current.txt
index 6325de7..31a6d4d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5347,18 +5347,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..ce6810f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -470,18 +470,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/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 647e0d0..8f8083e 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();
+ }
+ }
+
+ /**
+ * Check whether the device is active.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * permission.
+ *
+ * @return the connected device that is active or null if no device
+ * is active
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public boolean isActiveDevice(@Nullable BluetoothDevice device) {
+ if (VDBG) log("isActiveDevice()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && ((device == null) || isValidDevice(device))) {
+ return mService.isActiveDevice(device);
+ }
+ 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();
+ }
+ }
+
+ /**
* Set priority of the profile
*
* <p> The device should already be paired.
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 4157845..dc3c765 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -305,6 +305,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
@@ -373,6 +386,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
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/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..c8efede 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -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");
}
@@ -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..9be3da6 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -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..311dc4c 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -62,17 +62,32 @@
/**
* Interface to send call-backs to the application when the service is connected.
*/
- public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+ public interface SecureElementListener {
+ /**
+ * Called by the framework when the service is connected.
+ */
+ void onServiceConnected();
+ }
+
+ /**
+ * 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 SecureElementListener mListener = null;
+
@Override
public IBinder asBinder() {
return this;
}
- /**
- * Called by the framework when the service is connected.
- */
- public void serviceConnected() {};
+ public void onServiceConnected() {
+ if (mListener != null) {
+ mListener.onServiceConnected();
+ }
+ }
}
+ private SEListener mSEListener = new SEListener();
private static final String TAG = "OMAPI.SEService";
@@ -95,34 +110,28 @@
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.
+ * onServiceConnected() 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 SecureElementListener object.
*/
- public SEService(Context context, SecureElementListener listener) {
+ public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
if (context == null) {
throw new NullPointerException("context must not be null");
}
mContext = context;
- mSEListener = listener;
+ mSEListener.mListener = listener;
mConnection = new ServiceConnection() {
@@ -131,9 +140,7 @@
mSecureElementService = ISecureElementService.Stub.asInterface(service);
if (mSEListener != null) {
- try {
- mSEListener.serviceConnected();
- } catch (RemoteException ignore) { }
+ mSEListener.onServiceConnected();
}
Log.i(TAG, "Service onServiceConnected");
}
@@ -233,7 +240,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..adfeddd 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -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");
}
@@ -223,32 +225,6 @@
}
/**
- * This method is provided to ease the development of mobile application and for compliancy
- * with existing applications.
- * This method is equivalent to openBasicChannel(aid, P2=0x00)
- *
- * @param aid the AID of the Applet to be selected on this channel, as a
- * byte array, or null if no Applet is to be selected.
- * @throws IOException if there is a communication problem to the reader or
- * the Secure Element.
- * @throws IllegalStateException if the Secure Element session is used after
- * being closed.
- * @throws IllegalArgumentException if the aid's length is not within 5 to
- * 16 (inclusive).
- * @throws SecurityException if the calling application cannot be granted
- * access to this AID or the default Applet on this
- * session.
- * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
- * selected.
- * @throws UnsupportedOperationException if the given P2 parameter is not
- * supported by the device
- * @return an instance of Channel if available or null.
- */
- public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
- return openBasicChannel(aid, (byte) 0x00);
- }
-
- /**
* Open a logical channel with the Secure Element, selecting the Applet represented by
* the given AID. If the AID is null, which means no Applet is to be selected on this
* channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -300,7 +276,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");
}
@@ -327,32 +304,4 @@
}
}
}
-
- /**
- * This method is provided to ease the development of mobile application and for compliancy
- * with existing applications.
- * This method is equivalent to openLogicalChannel(aid, P2=0x00)
- *
- * @param aid the AID of the Applet to be selected on this channel, as a
- * byte array.
- * @throws IOException if there is a communication problem to the reader or
- * the Secure Element.
- * @throws IllegalStateException if the Secure Element is used after being
- * closed.
- * @throws IllegalArgumentException if the aid's length is not within 5 to
- * 16 (inclusive).
- * @throws SecurityException if the calling application cannot be granted
- * access to this AID or the default Applet on this
- * session.
- * @throws NoSuchElementException if the AID on the Secure Element is not
- * available or cannot be selected or a logical channel is already
- * open to a non-multiselectable Applet.
- * @throws UnsupportedOperationException if the given P2 parameter is not
- * supported by the device.
- * @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 {
- return openLogicalChannel(aid, (byte) 0x00);
- }
}
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/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/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 4db0034..7828c4c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -42,6 +42,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;
@@ -147,6 +148,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/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 428f0b8..b83b8e1 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] -->
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..6c068ff 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.isActiveDevice(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..920500f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -134,6 +134,17 @@
return mService.getConnectionState(device);
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ mService.setActiveDevice(device);
+ return true;
+ }
+
+ public boolean isActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.isActiveDevice(device);
+ }
+
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f24d9f..6b4cd4b 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() {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index cffa834..9581308 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -181,6 +181,7 @@
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;
@@ -202,12 +203,13 @@
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();
@@ -215,8 +217,8 @@
deps.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();
@@ -1240,7 +1242,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 +1262,7 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+ mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
setInitialState(mInitialState);
@@ -1996,7 +1998,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/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..e4c7ca0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -117,6 +117,8 @@
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
@@ -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;
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..66afb0f 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,26 @@
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();
+ }
}
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/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 81a966b..cdab521 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -31,11 +31,13 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.DownloadStatusListener;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.InternalDownloadProgressListener;
import android.telephony.mbms.InternalDownloadSessionCallback;
-import android.telephony.mbms.InternalDownloadStateCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsDownloadReceiver;
import android.telephony.mbms.MbmsErrors;
@@ -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) {
@@ -569,34 +573,33 @@
}
/**
- * 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}.
*
* @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.
+ * @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.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/
- public int registerStateCallback(@NonNull DownloadRequest request,
- @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
+ public int 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.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
@@ -608,40 +611,41 @@
sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
- mInternalDownloadCallbacks.put(callback, internalCallback);
+ mInternalDownloadStatusListeners.put(listener, internalListener);
return MbmsErrors.SUCCESS;
+
}
/**
- * 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}.
*
* @param request The {@link DownloadRequest} provided during registration
- * @param callback The callback provided during registration.
+ * @param listener The listener provided during registration.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/
- public int unregisterStateCallback(@NonNull DownloadRequest request,
- @NonNull DownloadStateCallback callback) {
+ public int 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.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
@@ -654,8 +658,102 @@
return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
} 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}.
+ *
+ * @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.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
+ */
+ public int 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.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ return result;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sIsInitialized.set(false);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ }
+ mInternalDownloadProgressListeners.put(listener, internalListener);
+ return MbmsErrors.SUCCESS;
+ }
+
+ /**
+ * 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}.
+ *
+ * @param request The {@link DownloadRequest} provided during registration
+ * @param listener The listener provided during registration.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
+ */
+ public int 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.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ return result;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sIsInitialized.set(false);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+ }
+ } finally {
+ InternalDownloadProgressListener internalCallback =
+ mInternalDownloadProgressListeners.remove(listener);
if (internalCallback != null) {
internalCallback.stop();
}
@@ -700,9 +798,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
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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f92f191..caf08e9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7668,11 +7668,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{}
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/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/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/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/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..f9d7161 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);
}
@@ -223,71 +228,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 +300,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/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/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd4..c727bd2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,8 +27,12 @@
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.assertNotNull;
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 +40,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 +50,29 @@
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 +90,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,13 +108,21 @@
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;
@@ -103,16 +133,21 @@
@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;
@@ -146,23 +181,104 @@
}
}
+ public class MockTetheringDependencies extends TetheringDependencies {
+ private StateMachine upstreamNetworkMonitorMasterSM;
+ private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+
+ @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);
+ }
+ }
+
+ private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6) {
+ final NetworkInfo info = new NetworkInfo(ConnectivityManager.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));
+ }
+
+
+ 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);
+ }
+
+ private static NetworkState buildMobileDualStackUpstreamState() {
+ return buildMobileUpstreamState(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);
@@ -176,8 +292,6 @@
};
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
- when(mTetheringDependencies.getOffloadHardwareInterface(
- any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
mLooper.getLooper(), mSystemProperties,
mTetheringDependencies);
@@ -264,10 +378,10 @@
}
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) {
@@ -287,7 +401,7 @@
// 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();
@@ -297,27 +411,31 @@
// broadcast indicating that the interface is "available".
if (emulateInterfaceStatusChanged) {
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
}
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
- @Test
- public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ private void prepareUsbTethering(NetworkState upstreamState) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+ 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);
@@ -328,6 +446,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
@@ -348,26 +470,21 @@
// 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, ConnectivityManager.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, ConnectivityManager.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);
@@ -375,14 +492,14 @@
// 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);
@@ -390,9 +507,62 @@
// 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));
+ 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);
+ 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_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 workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
workingLocalOnlyHotspotEnrichedApBroadcast(true);
@@ -420,12 +590,12 @@
// 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);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
@@ -448,30 +618,23 @@
// 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, ConnectivityManager.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, ConnectivityManager.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);
@@ -494,14 +657,14 @@
// 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);
@@ -509,7 +672,7 @@
// 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));
+ mTethering.getLastTetherError(TEST_WLAN_IFNAME));
}
// TODO: Test with and without interfaceStatusChanged().
@@ -530,21 +693,21 @@
// 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);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.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,9 +715,9 @@
// 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);
@@ -596,7 +759,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 +781,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 +792,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/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..5f3fc54 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,7 +38,6 @@
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;
@@ -75,6 +73,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 +83,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 +110,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 +346,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);
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());