Merge changes I303d1102,I72c9aa43,If13a5284
* changes:
Fix default network validation overcounting
Prevent crash in NetworkManagementServiceTest#shutdown()
Fix ApfTest
diff --git a/Android.bp b/Android.bp
index 6712461..69fb459 100644
--- a/Android.bp
+++ b/Android.bp
@@ -200,6 +200,11 @@
"core/java/android/nfc/INfcUnlockHandler.aidl",
"core/java/android/nfc/INfcDta.aidl",
"core/java/android/nfc/ITagRemovedCallback.aidl",
+ "core/java/android/se/omapi/ISecureElementService.aidl",
+ "core/java/android/se/omapi/ISecureElementListener.aidl",
+ "core/java/android/se/omapi/ISecureElementChannel.aidl",
+ "core/java/android/se/omapi/ISecureElementReader.aidl",
+ "core/java/android/se/omapi/ISecureElementSession.aidl",
"core/java/android/os/IBatteryPropertiesListener.aidl",
"core/java/android/os/IBatteryPropertiesRegistrar.aidl",
"core/java/android/os/ICancellationSignal.aidl",
diff --git a/Android.mk b/Android.mk
index 5dfa81c..ea75b19 100644
--- a/Android.mk
+++ b/Android.mk
@@ -697,6 +697,38 @@
include $(BUILD_HOST_JAVA_LIBRARY)
+# ==== hiddenapi lists =======================================
+
+# Copy blacklist and dark greylist over into the build folder.
+# This is for ART buildbots which need to mock these lists and have alternative
+# rules for building them. Other rules in the build system should depend on the
+# files in the build folder.
+
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+
+# Generate light greylist as private API minus (blacklist plus dark greylist).
+
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+ exit 1; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
+ echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 2; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 3; \
+ fi
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+
# Include subdirectory makefiles
# ============================================================
diff --git a/api/current.txt b/api/current.txt
index 33d3bb9..2b8b6f5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -92,6 +92,7 @@
field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
field public static final java.lang.String NFC = "android.permission.NFC";
+ field public static final java.lang.String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
@@ -37013,6 +37014,59 @@
}
+package android.se.omapi {
+
+ public class Channel {
+ method public void close();
+ method public byte[] getSelectResponse();
+ method public android.se.omapi.Session getSession();
+ method public boolean isBasicChannel();
+ method public boolean isClosed();
+ method public boolean selectNext() throws java.io.IOException;
+ method public byte[] transmit(byte[]) throws java.io.IOException;
+ }
+
+ public abstract interface ISecureElementListener implements android.os.IInterface {
+ method public abstract void serviceConnected() throws android.os.RemoteException;
+ }
+
+ public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
+ ctor public ISecureElementListener.Stub();
+ method public android.os.IBinder asBinder();
+ method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ }
+
+ public class Reader {
+ method public void closeSessions();
+ method public java.lang.String getName();
+ method public android.se.omapi.SEService getSEService();
+ method public boolean isSecureElementPresent();
+ method public android.se.omapi.Session openSession() throws java.io.IOException;
+ }
+
+ public class SEService {
+ ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
+ method public android.se.omapi.Reader[] getReaders();
+ method public java.lang.String getVersion();
+ method public boolean isConnected();
+ method public void shutdown();
+ }
+
+ public class Session {
+ method public void close();
+ method public void closeChannels();
+ method public byte[] getATR();
+ 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;
+ }
+
+}
+
package android.security {
public final class KeyChain {
@@ -38406,11 +38460,6 @@
field public final int errno;
}
- public class Int32Ref {
- ctor public Int32Ref(int);
- field public int value;
- }
-
public class Int64Ref {
ctor public Int64Ref(long);
field public long value;
@@ -38510,7 +38559,6 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -40174,6 +40222,7 @@
}
public final class CellIdentityLte extends android.telephony.CellIdentity {
+ method public int getBandwidth();
method public int getCi();
method public int getEarfcn();
method public deprecated int getMcc();
@@ -40217,8 +40266,13 @@
public abstract class CellInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getCellConnectionStatus();
method public long getTimeStamp();
method public boolean isRegistered();
+ field public static final int CONNECTION_NONE = 0; // 0x0
+ field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+ field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+ field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
}
@@ -40531,6 +40585,9 @@
ctor public ServiceState(android.os.Parcel);
method protected void copyFrom(android.telephony.ServiceState);
method public int describeContents();
+ method public int[] getCellBandwidths();
+ method public int getChannelNumber();
+ method public int getDuplexMode();
method public boolean getIsManualSelection();
method public int getNetworkId();
method public java.lang.String getOperatorAlphaLong();
@@ -40547,6 +40604,9 @@
method public void setStateOutOfService();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.ServiceState> CREATOR;
+ field public static final int DUPLEX_MODE_FDD = 1; // 0x1
+ field public static final int DUPLEX_MODE_TDD = 2; // 0x2
+ field public static final int DUPLEX_MODE_UNKNOWN = 0; // 0x0
field public static final int STATE_EMERGENCY_ONLY = 2; // 0x2
field public static final int STATE_IN_SERVICE = 0; // 0x0
field public static final int STATE_OUT_OF_SERVICE = 1; // 0x1
@@ -44309,42 +44369,42 @@
method public void previousMonth();
}
- public final class MutableBoolean {
+ public final deprecated class MutableBoolean {
ctor public MutableBoolean(boolean);
field public boolean value;
}
- public final class MutableByte {
+ public final deprecated class MutableByte {
ctor public MutableByte(byte);
field public byte value;
}
- public final class MutableChar {
+ public final deprecated class MutableChar {
ctor public MutableChar(char);
field public char value;
}
- public final class MutableDouble {
+ public final deprecated class MutableDouble {
ctor public MutableDouble(double);
field public double value;
}
- public final class MutableFloat {
+ public final deprecated class MutableFloat {
ctor public MutableFloat(float);
field public float value;
}
- public final class MutableInt {
+ public final deprecated class MutableInt {
ctor public MutableInt(int);
field public int value;
}
- public final class MutableLong {
+ public final deprecated class MutableLong {
ctor public MutableLong(long);
field public long value;
}
- public final class MutableShort {
+ public final deprecated class MutableShort {
ctor public MutableShort(short);
field public short value;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 545438c..041c213 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -697,6 +697,7 @@
field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+ field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String VR_SERVICE = "vrmanager";
field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -2587,9 +2588,23 @@
method public java.lang.String getInterfaceName();
}
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void startNattKeepalive(android.net.IpSecTransform.NattKeepaliveCallback, int, android.os.Handler) throws java.io.IOException;
+ method public void stopNattKeepalive();
+ }
+
public static class IpSecTransform.Builder {
method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public android.net.IpSecTransform.Builder setNattKeepalive(int);
+ }
+
+ public static class IpSecTransform.NattKeepaliveCallback {
+ ctor public IpSecTransform.NattKeepaliveCallback();
+ method public void onError(int);
+ method public void onStarted();
+ method public void onStopped();
+ field public static final int ERROR_HARDWARE_ERROR = 3; // 0x3
+ field public static final int ERROR_HARDWARE_UNSUPPORTED = 2; // 0x2
+ field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
}
public class NetworkKey implements android.os.Parcelable {
@@ -3973,7 +3988,9 @@
method public int describeContents();
method public int getAccessNetworkTechnology();
method public int[] getAvailableServices();
+ method public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
+ method public int getReasonForDenial();
method public int getRegState();
method public int getTransportType();
method public boolean isEmergencyEnabled();
diff --git a/config/hiddenapi-blacklist.txt b/config/hiddenapi-blacklist.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/hiddenapi-blacklist.txt
diff --git a/config/hiddenapi-dark-greylist.txt b/config/hiddenapi-dark-greylist.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/hiddenapi-dark-greylist.txt
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index dbdb81c..3abb24c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -128,6 +128,8 @@
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
+import dalvik.system.VMRuntime;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -7039,11 +7041,12 @@
mFragments.dispatchStart();
mFragments.reportLoaderStart();
- // This property is set for all builds except final release
- boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
boolean isAppDebuggable =
(mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ // This property is set for all non-user builds except final release
+ boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
if (isAppDebuggable || isDlwarningEnabled) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
@@ -7064,6 +7067,28 @@
}
}
+ // This property is set for all non-user builds except final release
+ boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
+
+ if (isAppDebuggable || isApiWarningEnabled) {
+ if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ String appName = getApplicationInfo().loadLabel(getPackageManager())
+ .toString();
+ String warning = "Detected problems with API compatiblity\n"
+ + "(please consult log for detail)";
+ if (isAppDebuggable) {
+ new AlertDialog.Builder(this)
+ .setTitle(appName)
+ .setMessage(warning)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .show();
+ } else {
+ Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
mActivityTransitionState.enterReady(this);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1811748..7b1afa5 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -137,6 +137,7 @@
void publishService(in IBinder token, in Intent intent, in IBinder service);
void activityResumed(in IBinder token);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+ void setAgentApp(in String packageName, @nullable String agent);
void setAlwaysFinish(boolean enabled);
boolean startInstrumentation(in ComponentName className, in String profileFile,
int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index 044b780..8f718df 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -85,6 +85,15 @@
}
/**
+ * Return a new ProfilerInfo instance, with fields populated from this object,
+ * and {@link agent} and {@link attachAgentDuringBind} as given.
+ */
+ public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+ return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+ this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+ }
+
+ /**
* Close profileFd, if it is open. The field will be null after a call to this function.
*/
public void closeFd() {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 35a21a4..b255a43 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -300,11 +300,7 @@
}
/**
- * Initiate connection to a profile of the remote bluetooth device.
- *
- * <p> Currently, the system supports only 1 connection to the
- * A2DP profile. The API will automatically disconnect connected
- * devices before connecting.
+ * Initiate connection to a profile of the remote Bluetooth device.
*
* <p> This API returns false in scenarios like the profile on the
* device is already connected or Bluetooth is not turned on.
@@ -699,15 +695,17 @@
/**
* Gets the current codec status (configuration and capability).
*
+ * @param device the remote Bluetooth device. If null, use the current
+ * active A2DP Bluetooth device.
* @return the current codec status
* @hide
*/
- public BluetoothCodecStatus getCodecStatus() {
- if (DBG) Log.d(TAG, "getCodecStatus");
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
- return mService.getCodecStatus();
+ return mService.getCodecStatus(device);
}
if (mService == null) {
Log.w(TAG, "Proxy not attached to service");
@@ -724,15 +722,18 @@
/**
* Sets the codec configuration preference.
*
+ * @param device the remote Bluetooth device. If null, use the current
+ * active A2DP Bluetooth device.
* @param codecConfig the codec configuration preference
* @hide
*/
- public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
- if (DBG) Log.d(TAG, "setCodecConfigPreference");
+ public void setCodecConfigPreference(BluetoothDevice device,
+ BluetoothCodecConfig codecConfig) {
+ if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
- mService.setCodecConfigPreference(codecConfig);
+ mService.setCodecConfigPreference(device, codecConfig);
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return;
@@ -747,36 +748,42 @@
/**
* Enables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @hide
*/
- public void enableOptionalCodecs() {
- if (DBG) Log.d(TAG, "enableOptionalCodecs");
- enableDisableOptionalCodecs(true);
+ public void enableOptionalCodecs(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+ enableDisableOptionalCodecs(device, true);
}
/**
* Disables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @hide
*/
- public void disableOptionalCodecs() {
- if (DBG) Log.d(TAG, "disableOptionalCodecs");
- enableDisableOptionalCodecs(false);
+ public void disableOptionalCodecs(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+ enableDisableOptionalCodecs(device, false);
}
/**
* Enables or disables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
- private void enableDisableOptionalCodecs(boolean enable) {
+ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
if (enable) {
- mService.enableOptionalCodecs();
+ mService.enableOptionalCodecs(device);
} else {
- mService.disableOptionalCodecs();
+ mService.disableOptionalCodecs(device);
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 70087da..2859326 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4062,6 +4062,16 @@
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.se.omapi.ISecureElementService}
+ * for accessing the SecureElementService.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 37e2c4f..9ccdbe2 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -17,11 +17,14 @@
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -128,13 +131,6 @@
int status = result.status;
checkResultStatus(status);
mResourceId = result.resourceId;
-
- /* Keepalive will silently fail if not needed by the config; but, if needed and
- * it fails to start, we need to bail because a transform will not be reliable
- * to use if keepalive is expected to offload and fails.
- */
- // FIXME: if keepalive fails, we need to fail spectacularly
- startKeepalive(mContext);
Log.d(TAG, "Added Transform with Id " + mResourceId);
mCloseGuard.open("build");
} catch (RemoteException e) {
@@ -164,13 +160,9 @@
return;
}
try {
- /* Order matters here because the keepalive is best-effort but could fail in some
- * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
- * still want to clear out the transform.
- */
IIpSecService svc = getIpSecService();
svc.deleteTransform(mResourceId);
- stopKeepalive();
+ stopNattKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
@@ -198,42 +190,35 @@
private final Context mContext;
private final CloseGuard mCloseGuard = CloseGuard.get();
private ConnectivityManager.PacketKeepalive mKeepalive;
- private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
- private Object mKeepaliveSyncLock = new Object();
- private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
+ private Handler mCallbackHandler;
+ private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
new ConnectivityManager.PacketKeepaliveCallback() {
@Override
public void onStarted() {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted());
}
}
@Override
public void onStopped() {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mKeepalive = null;
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped());
}
}
@Override
public void onError(int error) {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = error;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mKeepalive = null;
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error));
}
}
};
- /* Package */
- void startKeepalive(Context c) {
- if (mConfig.getNattKeepaliveInterval() != 0) {
- Log.wtf(TAG, "Keepalive not yet supported.");
- }
- }
+ private NattKeepaliveCallback mUserKeepaliveCallback;
/** @hide */
@VisibleForTesting
@@ -241,9 +226,93 @@
return mResourceId;
}
- /* Package */
- void stopKeepalive() {
- return;
+ /**
+ * A callback class to provide status information regarding a NAT-T keepalive session
+ *
+ * <p>Use this callback to receive status information regarding a NAT-T keepalive session
+ * by registering it when calling {@link #startNattKeepalive}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static class NattKeepaliveCallback {
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = 1;
+ /** The hardware does not support this request. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = 2;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = 3;
+
+ /** The requested keepalive was successfully started. */
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ public void onStopped() {}
+ /** An error occurred. */
+ public void onError(int error) {}
+ }
+
+ /**
+ * Start a NAT-T keepalive session for the current transform.
+ *
+ * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides
+ * a power efficient mechanism of sending NAT-T packets at a specified interval.
+ *
+ * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status
+ * information about the requested NAT-T keepalive session.
+ * @param intervalSeconds the interval between NAT-T keepalives being sent. The
+ * the allowed range is between 20 and 3600 seconds.
+ * @param handler a handler on which to post callbacks when received.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
+ int intervalSeconds, @NonNull Handler handler) throws IOException {
+ checkNotNull(userCallback);
+ if (intervalSeconds < 20 || intervalSeconds > 3600) {
+ throw new IllegalArgumentException("Invalid NAT-T keepalive interval");
+ }
+ checkNotNull(handler);
+ if (mResourceId == INVALID_RESOURCE_ID) {
+ throw new IllegalStateException(
+ "Packet keepalive cannot be started for an inactive transform");
+ }
+
+ synchronized (mKeepaliveCallback) {
+ if (mKeepaliveCallback != null) {
+ throw new IllegalStateException("Keepalive already active");
+ }
+
+ mUserKeepaliveCallback = userCallback;
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ mKeepalive = cm.startNattKeepalive(
+ mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback,
+ NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()),
+ 4500, // FIXME urgently, we need to get the port number from the Encap socket
+ NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress()));
+ mCallbackHandler = handler;
+ }
+ }
+
+ /**
+ * Stop an ongoing NAT-T keepalive session.
+ *
+ * Calling this API will request that an ongoing NAT-T keepalive session be terminated.
+ * If this API is not called when a Transform is closed, the underlying NAT-T session will
+ * be terminated automatically.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void stopNattKeepalive() {
+ synchronized (mKeepaliveCallback) {
+ if (mKeepalive == null) {
+ Log.e(TAG, "No active keepalive to stop");
+ return;
+ }
+ mKeepalive.stop();
+ }
}
/** This class is used to build {@link IpSecTransform} objects. */
@@ -323,26 +392,6 @@
return this;
}
- // TODO: Decrease the minimum keepalive to maybe 10?
- // TODO: Probably a better exception to throw for NATTKeepalive failure
- // TODO: Specify the needed NATT keepalive permission.
- /**
- * Set NAT-T keepalives to be sent with a given interval.
- *
- * <p>This will set power-efficient keepalive packets to be sent by the system. If NAT-T
- * keepalive is requested but cannot be activated, then creation of an {@link
- * IpSecTransform} will fail when calling the build method.
- *
- * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
- * between 20s and 3600s.
- * @hide
- */
- @SystemApi
- public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
- mConfig.setNattKeepaliveInterval(intervalSeconds);
- return this;
- }
-
/**
* Build a transport mode {@link IpSecTransform}.
*
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 31516fd..e81ed9a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -892,7 +892,7 @@
*
* @hide
*/
- private Set<UidRange> mUids = null;
+ private ArraySet<UidRange> mUids = null;
/**
* Convenience method to set the UIDs this network applies to to a single UID.
@@ -1180,7 +1180,7 @@
dest.writeInt(mLinkDownBandwidthKbps);
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength);
- dest.writeArraySet(new ArraySet<>(mUids));
+ dest.writeArraySet(mUids);
}
public static final Creator<NetworkCapabilities> CREATOR =
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2b662a0..2425bba 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,13 @@
}
}
+ /** Accumulate a single netd sock_diag poll result reported by netd. */
+ public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+ pendingSummary.tcpLossRate.count(lost, sent);
+ pendingSummary.roundTripTimeUs.count(rttUs);
+ pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+ }
+
/** Represents running sums for dns and connect average error counts and average latencies. */
public static class Summary {
@@ -109,6 +116,13 @@
public final Metrics connectLatencies = new Metrics();
// Blocking and non blocking connect error rate measured in percentage points.
public final Metrics connectErrorRate = new Metrics();
+ // TCP socket packet loss stats collected from Netlink sock_diag.
+ public final Metrics tcpLossRate = new Metrics();
+ // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+ public final Metrics roundTripTimeUs = new Metrics();
+ // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+ // differences between last packet sent timestamp and last ack received timestamp.
+ public final Metrics sentAckTimeDiffenceMs = new Metrics();
public Summary(int netId, long transports) {
this.netId = netId;
@@ -120,6 +134,7 @@
dnsErrorRate.merge(that.dnsErrorRate);
connectLatencies.merge(that.connectLatencies);
connectErrorRate.merge(that.connectErrorRate);
+ tcpLossRate.merge(that.tcpLossRate);
}
@Override
@@ -135,6 +150,10 @@
j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
(int) connectLatencies.average(), (int) connectLatencies.max,
100 * connectErrorRate.average(), connectErrorRate.count));
+ j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+ 100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+ j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+ j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
return j.toString();
}
}
@@ -152,7 +171,11 @@
}
void count(double value) {
- count++;
+ count(value, 1);
+ }
+
+ void count(double value, int subcount) {
+ count += subcount;
sum += value;
max = Math.max(max, value);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9ad5ca4..5e6f5f5 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,12 @@
*/
public static final int OTA_UPDATE_UID = 1061;
+ /**
+ * Defines the UID/GID for the Secure Element service process.
+ * @hide
+ */
+ public static final int SE_UID = 1068;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
new file mode 100644
index 0000000..f0b9fa2d
--- /dev/null
+++ b/core/java/android/se/omapi/Channel.java
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
+ * Secure Element. It can be either a logical channel or the basic channel. They
+ * can be used to send APDUs to the secure element. Channels are opened by
+ * calling the Session.openBasicChannel(byte[]) or
+ * Session.openLogicalChannel(byte[]) methods.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Channel {
+
+ private static final String TAG = "OMAPI.Channel";
+ private Session mSession;
+ private final ISecureElementChannel mChannel;
+ private final SEService mService;
+ private final Object mLock = new Object();
+
+ Channel(SEService service, Session session, ISecureElementChannel channel) {
+ if (service == null || session == null || channel == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mService = service;
+ mSession = session;
+ mChannel = channel;
+ }
+
+ /**
+ * Closes this channel to the Secure Element. If the method is called when
+ * the channel is already closed, this method will be ignored. The close()
+ * method shall wait for completion of any pending transmit(byte[] command)
+ * before closing the channel.
+ */
+ public void close() {
+ if (!isClosed()) {
+ synchronized (mLock) {
+ try {
+ mChannel.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Error closing channel", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tells if this channel is closed.
+ *
+ * @return <code>true</code> if the channel is closed or in case of an error.
+ * <code>false</code> otherwise.
+ */
+ public boolean isClosed() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return true;
+ }
+ try {
+ return mChannel.isClosed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in isClosed()");
+ return true;
+ }
+ }
+
+ /**
+ * Returns a boolean telling if this channel is the basic channel.
+ *
+ * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+ * this channel is a logical channel.
+ */
+ public boolean isBasicChannel() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mChannel.isBasicChannel();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
+ * underlying layers generate as many TPDUs as necessary to transport this APDU. The
+ * API shall ensure that all available data returned from Secure Element, including
+ * concatenated responses, are retrieved and made available to the calling application. If a
+ * warning status code is received the API wont check for further response data but will
+ * return all data received so far and the warning status code.<br>
+ * The transport part is invisible from the application. The generated response is the
+ * response of the APDU which means that all protocols related responses are handled
+ * inside the API or the underlying implementation.<br>
+ * The transmit method shall support extended length APDU commands independently of
+ * the coding within the ATR.<br>
+ * For status word '61 XX' the API or underlying implementation shall issue a GET
+ * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
+ * word '6C XX', the API or underlying implementation shall reissue the input command
+ * with LE=XX. For other status words, the API (or underlying implementation) shall return
+ * the complete response including data and status word to the device application. The API
+ * (or underlying implementation) shall not handle internally the received status words. The
+ * channel shall not be closed even if the Secure Element answered with an error code.
+ * The system ensures the synchronization between all the concurrent calls to this method,
+ * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
+ * might be required to transport it to the SE. The entire APDU communication to this SE is
+ * locked to the APDU.<br>
+ * The channel information in the class byte in the APDU will be ignored. The system will
+ * add any required information to ensure the APDU is transported on this channel.
+ * The only restrictions on the set of commands that can be sent is defined below, the API
+ * implementation shall be able to send all other commands: <br>
+ * <ul>
+ * <li>MANAGE_CHANNEL commands are not allowed.</li>
+ * <li>SELECT by DF Name (p1=04) are not allowed.</li>
+ * <li>CLA bytes with channel numbers are de-masked.</li>
+ * </ul>
+ *
+ * @param command the APDU command to be transmitted, as a byte array.
+ *
+ * @return the response received, as a byte array. The returned byte array contains the data
+ * bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ *
+ * @throws IOException if there is a communication problem to the reader or the Secure Element.
+ * @throws IllegalStateException if the channel is used after being closed.
+ * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
+ * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
+ * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
+ * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
+ * @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 {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ byte[] response = mChannel.transmit(command);
+ if (response == null) {
+ throw new IOException("Error in communicating with Secure Element");
+ }
+ return response;
+ } catch (RemoteException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Get the session that has opened this channel.
+ *
+ * @return the session object this channel is bound to.
+ */
+ public @NonNull Session getSession() {
+ return mSession;
+ }
+
+ /**
+ * Returns the data as received from the application select command inclusively the status word
+ * received at applet selection.
+ * The returned byte array contains the data bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ * @return The data as returned by the application select command inclusively the status word.
+ * Only the status word if the application select command has no returned data.
+ * Returns null if an application select command has not been performed or the selection
+ * response can not be retrieved by the reader implementation.
+ */
+ public @Nullable byte[] getSelectResponse() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ byte[] response;
+ try {
+ response = mChannel.getSelectResponse();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+
+ if (response != null && response.length == 0) {
+ response = null;
+ }
+ return response;
+ }
+
+ /**
+ * Performs a selection of the next Applet on this channel that matches to the partial AID
+ * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
+ * This mechanism can be used by a device application to iterate through all Applets
+ * matching to the same partial AID.
+ * If selectNext() returns true a new Applet was successfully selected on this channel.
+ * If no further Applet exists with matches to the partial AID this method returns false
+ * and the already selected Applet stays selected. <br>
+ *
+ * Since the API cannot distinguish between a partial and full AID the API shall rely on the
+ * response of the Secure Element for the return value of this method. <br>
+ * The implementation of the underlying SELECT command within this method shall use
+ * the same values as the corresponding openBasicChannel(byte[] aid) or
+ * openLogicalChannel(byte[] aid) command with the option: <br>
+ * P2='02' (Next occurrence) <br>
+ * The select response stored in the Channel object shall be updated with the APDU
+ * response of the SELECT command.
+
+ * @return <code>true</code> if new Applet was selected on this channel.
+ <code>false</code> he already selected Applet stays selected on this channel.
+ *
+ * @throws IOException if there is a communication problem to the reader or the Secure Element.
+ * @throws IllegalStateException if the channel is used after being closed.
+ * @throws UnsupportedOperationException if this operation is not supported by the card.
+ */
+ public boolean selectNext() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ synchronized (mLock) {
+ return mChannel.selectNext();
+ }
+ } catch (RemoteException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/core/java/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 0000000..4ae57ab
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementChannel {
+
+ /**
+ * Closes the specified connection and frees internal resources.
+ * A logical channel will be closed.
+ */
+ void close();
+
+ /**
+ * Tells if this channel is closed.
+ *
+ * @return <code>true</code> if the channel is closed,
+ * <code>false</code> otherwise.
+ */
+ boolean isClosed();
+
+ /**
+ * Returns a boolean telling if this channel is the basic channel.
+ *
+ * @return <code>true</code> if this channel is a basic channel.
+ * <code>false</code> if this channel is a logical channel.
+ */
+ boolean isBasicChannel();
+
+ /**
+ * Returns the data as received from the application select command
+ * inclusively the status word. The returned byte array contains the data
+ * bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ */
+ byte[] getSelectResponse();
+
+ /**
+ * Transmits the specified command APDU and returns the response APDU.
+ * MANAGE channel commands are not supported.
+ * Selection of applets is not supported in logical channels.
+ */
+ byte[] transmit(in byte[] command);
+
+ /**
+ * Performs a selection of the next Applet on this channel that matches to
+ * the partial AID specified in the openBasicChannel(byte[] aid) or
+ * openLogicalChannel(byte[] aid) method. This mechanism can be used by a
+ * device application to iterate through all Applets matching to the same
+ * partial AID.
+ * If selectNext() returns true a new Applet was successfully selected on
+ * this channel.
+ * If no further Applet exists with matches to the partial AID this method
+ * returns false and the already selected Applet stays selected.
+ *
+ * @return <code>true</code> if new Applet was successfully selected.
+ * <code>false</code> if no further Applet exists which matches the
+ * partial AID.
+ */
+ boolean selectNext();
+}
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 0000000..3a99d63
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+/**
+ * Interface to receive call-backs when the service is connected.
+ */
+interface ISecureElementListener {
+ /**
+ * Called by the framework when the service is connected.
+ */
+ void serviceConnected();
+}
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 0000000..a312c44
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementReader {
+
+ /**
+ * Returns true if a card is present in the specified reader.
+ * Returns false if a card is not present in the specified reader.
+ */
+ boolean isSecureElementPresent();
+
+ /**
+ * Connects to a secure element in this reader. <br>
+ * This method prepares (initialises) the Secure Element for communication
+ * before the Session object is returned (e.g. powers the Secure Element by
+ * ICC ON if its not already on). There might be multiple sessions opened at
+ * the same time on the same reader. The system ensures the interleaving of
+ * APDUs between the respective sessions.
+ *
+ * @return a Session object to be used to create Channels.
+ */
+ ISecureElementSession openSession();
+
+ /**
+ * Close all the sessions opened on this reader. All the channels opened by
+ * all these sessions will be closed.
+ */
+ void closeSessions();
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/core/java/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 0000000..4fa799e
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementReader;
+
+/**
+ * SecureElement service interface.
+ * @hide
+ */
+interface ISecureElementService {
+
+ /**
+ * Returns the friendly names of available Secure Element readers.
+ */
+ String[] getReaders();
+
+ /**
+ * Returns SecureElement Service reader object to the given name.
+ */
+ ISecureElementReader getReader(String reader);
+
+ /**
+ * Checks if the application defined by the package name is allowed to
+ * receive NFC transaction events for the defined AID.
+ */
+ boolean[] isNFCEventAllowed(String reader, in byte[] aid,
+ in String[] packageNames);
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/core/java/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 0000000..8ea599f
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementChannel;
+import android.se.omapi.ISecureElementReader;
+import android.se.omapi.ISecureElementListener;
+
+/** @hide */
+interface ISecureElementSession {
+
+ /**
+ * Returns the ATR of the connected card or null if the ATR is not available
+ */
+ byte[] getAtr();
+
+ /**
+ * Close the connection with the Secure Element. This will close any
+ * channels opened by this application with this Secure Element.
+ */
+ void close();
+
+ /**
+ * Close any channel opened on this session.
+ */
+ void closeChannels();
+
+
+ /**
+ * Tells if this session is closed.
+ *
+ * @return <code>true</code> if the session is closed, false otherwise.
+ */
+ boolean isClosed();
+
+ /**
+ * Opens a connection using the basic channel of the card in the
+ * specified reader and returns a channel handle. Selects the specified
+ * applet if aid != null.
+ * Logical channels cannot be opened with this connection.
+ * Use interface method openLogicalChannel() to open a logical channel.
+ */
+ ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
+ ISecureElementListener listener);
+
+ /**
+ * Opens a connection using the next free logical channel of the card in the
+ * specified reader. Selects the specified applet.
+ * Selection of other applets with this connection is not supported.
+ */
+ ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
+ ISecureElementListener listener);
+}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
new file mode 100644
index 0000000..9f15739
--- /dev/null
+++ b/core/java/android/se/omapi/Reader.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent Secure Element Readers supported to this
+ * device. These Readers can be physical devices or virtual devices. They can be
+ * removable or not. They can contain Secure Element that can or cannot be
+ * removed.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Reader {
+
+ private static final String TAG = "OMAPI.Reader";
+ private final String mName;
+ private final SEService mService;
+ private ISecureElementReader mReader;
+ private final Object mLock = new Object();
+
+
+ Reader(SEService service, String name, ISecureElementReader reader) throws
+ IOException {
+ if (reader == null || service == null || name == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mName = name;
+ mService = service;
+ mReader = reader;
+ }
+
+ /**
+ * Return the name of this reader.
+ * <ul>
+ * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+ * The slot number “1” for a reader is optional
+ * (SIM and SIM1 are both valid for the first SIM-reader,
+ * but if there are two readers then the second reader must be named SIM2).
+ * This applies also for other SD or SE readers.
+ *
+ * @return the reader name, as a String.
+ */
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ /**
+ * Connects to a Secure Element in this reader. <br>
+ * This method prepares (initialises) the Secure Element for communication
+ * before the Session object is returned (e.g. powers the Secure Element by
+ * ICC ON if its not already on). There might be multiple sessions opened at
+ * the same time on the same reader. The system ensures the interleaving of
+ * APDUs between the respective sessions.
+ *
+ * @throws IOException if something went wrong with the communicating to the
+ * Secure Element or the reader.
+ * @return a Session object to be used to create Channels.
+ */
+ public @NonNull Session openSession() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ synchronized (mLock) {
+ ISecureElementSession session;
+ try {
+ session = mReader.openSession();
+ } catch (RemoteException e) {
+ throw new IOException(e.getMessage());
+ }
+ if (session == null) {
+ throw new IOException("service session is null.");
+ }
+ return new Session(mService, session, this);
+ }
+ }
+
+ /**
+ * Check if a Secure Element is present in this reader.
+ *
+ * @throws IllegalStateException if the service is not connected
+ * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+ */
+ public boolean isSecureElementPresent() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ try {
+ return mReader.isSecureElementPresent();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Error in isSecureElementPresent()");
+ }
+ }
+
+ /**
+ * Return the Secure Element service this reader is bound to.
+ *
+ * @return the SEService object.
+ */
+ public @NonNull SEService getSEService() {
+ return mService;
+ }
+
+ /**
+ * Close all the sessions opened on this reader.
+ * All the channels opened by all these sessions will be closed.
+ */
+ public void closeSessions() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mReader.closeSessions();
+ } catch (RemoteException ignore) { }
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
new file mode 100644
index 0000000..1e37277d
--- /dev/null
+++ b/core/java/android/se/omapi/SEService.java
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * The SEService realises the communication to available Secure Elements on the
+ * device. This is the entry point of this API. It is used to connect to the
+ * infrastructure and get access to a list of Secure Element Readers.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
+ */
+public class SEService {
+
+ private static final String TAG = "OMAPI.SEService";
+
+ private final Object mLock = new Object();
+
+ /** The client context (e.g. activity). */
+ private final Context mContext;
+
+ /** The backend system. */
+ private volatile ISecureElementService mSecureElementService;
+
+ /**
+ * Class for interacting with the main interface of the backend.
+ */
+ private ServiceConnection mConnection;
+
+ /**
+ * Collection of available readers
+ */
+ 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.
+ *
+ * @param context
+ * the context of the calling application. Cannot be
+ * <code>null</code>.
+ * @param listener
+ * a ISecureElementListener object. Can be <code>null</code>.
+ */
+ public SEService(Context context, ISecureElementListener listener) {
+
+ if (context == null) {
+ throw new NullPointerException("context must not be null");
+ }
+
+ mContext = context;
+ mSEListener = listener;
+
+ mConnection = new ServiceConnection() {
+
+ public synchronized void onServiceConnected(
+ ComponentName className, IBinder service) {
+
+ mSecureElementService = ISecureElementService.Stub.asInterface(service);
+ if (mSEListener != null) {
+ try {
+ mSEListener.serviceConnected();
+ } catch (RemoteException ignore) { }
+ }
+ Log.i(TAG, "Service onServiceConnected");
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mSecureElementService = null;
+ Log.i(TAG, "Service onServiceDisconnected");
+ }
+ };
+
+ Intent intent = new Intent(ISecureElementService.class.getName());
+ intent.setClassName("com.android.se",
+ "com.android.se.SecureElementService");
+ boolean bindingSuccessful =
+ mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ if (bindingSuccessful) {
+ Log.i(TAG, "bindService successful");
+ }
+ }
+
+ /**
+ * Tells whether or not the service is connected.
+ *
+ * @return <code>true</code> if the service is connected.
+ */
+ public boolean isConnected() {
+ return mSecureElementService != null;
+ }
+
+ /**
+ * Returns the list of available Secure Element readers.
+ * There must be no duplicated objects in the returned list.
+ * All available readers shall be listed even if no card is inserted.
+ *
+ * @return The readers list, as an array of Readers. If there are no
+ * readers the returned array is of length 0.
+ */
+ public @NonNull Reader[] getReaders() {
+ if (mSecureElementService == null) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ String[] readerNames;
+ try {
+ readerNames = mSecureElementService.getReaders();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ Reader[] readers = new Reader[readerNames.length];
+ int i = 0;
+ for (String readerName : readerNames) {
+ if (mReaders.get(readerName) == null) {
+ try {
+ mReaders.put(readerName, new Reader(this, readerName,
+ getReader(readerName)));
+ readers[i++] = mReaders.get(readerName);
+ } catch (Exception e) {
+ Log.e(TAG, "Error adding Reader: " + readerName, e);
+ }
+ } else {
+ readers[i++] = mReaders.get(readerName);
+ }
+ }
+ return readers;
+ }
+
+ /**
+ * Releases all Secure Elements resources allocated by this SEService
+ * (including any binding to an underlying service).
+ * As a result isConnected() will return false after shutdown() was called.
+ * After this method call, the SEService object is not connected.
+ * It is recommended to call this method in the termination method of the calling application
+ * (or part of this application) which is bound to this SEService.
+ */
+ public void shutdown() {
+ synchronized (mLock) {
+ if (mSecureElementService != null) {
+ for (Reader reader : mReaders.values()) {
+ try {
+ reader.closeSessions();
+ } catch (Exception ignore) { }
+ }
+ }
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ // Do nothing and fail silently since an error here indicates
+ // that binding never succeeded in the first place.
+ }
+ mSecureElementService = null;
+ }
+ }
+
+ /**
+ * Returns the version of the OpenMobile API specification this
+ * implementation is based on.
+ *
+ * @return String containing the OpenMobile API version (e.g. "3.0").
+ */
+ public String getVersion() {
+ return "3.2";
+ }
+
+ @NonNull ISecureElementListener getListener() {
+ return mSEListener;
+ }
+
+ /**
+ * Obtain a Reader instance from the SecureElementService
+ */
+ private @NonNull ISecureElementReader getReader(String name) {
+ try {
+ return mSecureElementService.getReader(name);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
new file mode 100644
index 0000000..bb2a032
--- /dev/null
+++ b/core/java/android/se/omapi/Session.java
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * Instances of this class represent a connection session to one of the Secure
+ * Elements available on the device. These objects can be used to get a
+ * communication channel with an Applet in the Secure Element.
+ * This channel can be the basic channel or a logical channel.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
+ */
+public class Session {
+
+ private final Object mLock = new Object();
+ private final SEService mService;
+ private final Reader mReader;
+ private final ISecureElementSession mSession;
+ private static final String TAG = "OMAPI.Session";
+
+ Session(SEService service, ISecureElementSession session, Reader reader) {
+ if (service == null || reader == null || session == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mService = service;
+ mReader = reader;
+ mSession = session;
+ }
+
+ /**
+ * Get the reader that provides this session.
+ *
+ * @return The Reader object.
+ */
+ public @NonNull Reader getReader() {
+ return mReader;
+ }
+
+ /**
+ * Get the Answer to Reset of this Secure Element. <br>
+ * The returned byte array can be null if the ATR for this Secure Element is
+ * not available.
+ *
+ * @throws IllegalStateException if there was an error connecting to SE or
+ * if the service was not connected.
+ * @return the ATR as a byte array or null.
+ */
+ public @Nullable byte[] getATR() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mSession.getAtr();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Close the connection with the Secure Element. This will close any
+ * channels opened by this application with this Secure Element.
+ */
+ public void close() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mSession.close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing session", e);
+ }
+ }
+ }
+
+ /**
+ * Tells if this session is closed.
+ *
+ * @return <code>true</code> if the session is closed, false otherwise.
+ */
+ public boolean isClosed() {
+ try {
+ return mSession.isClosed();
+ } catch (RemoteException e) {
+ // If there was an error here, then the session is considered close
+ return true;
+ }
+ }
+
+ /**
+ * Close any channel opened on this session.
+ */
+ public void closeChannels() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ mSession.closeChannels();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing channels", e);
+ }
+ }
+ }
+
+ /**
+ * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the
+ * one that has number 0). The obtained object is an instance of the Channel class.
+ * If the AID is null, it means no Applet is to be selected on this channel and the default
+ * Applet is used. If the AID is defined then the corresponding Applet is selected.
+ * Once this channel has been opened by a device application, it is considered as "locked"
+ * by this device application, and other calls to this method will return null, until the
+ * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel
+ * locked (i.e. return null to applications), to prevent access to the basic channel, while
+ * some other might return a channel object implementing some kind of filtering on the
+ * commands, restricting the set of accepted command to a smaller set.
+ * It is recommended for the UICC to reject the opening of the basic channel to a specific
+ * applet, by always answering null to such a request.
+ * For other Secure Elements, the recommendation is to accept opening the basic channel
+ * on the default applet until another applet is selected on the basic channel. As there is no
+ * other way than a reset to select again the default applet, the implementation of the
+ * transport API should guarantee that the openBasicChannel(null) command will return
+ * null until a reset occurs.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ * <ul>
+ * <li>CLA = '00'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2='00'.
+ *
+ * @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.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @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, byte p2) throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (RemoteException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 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
+ * logical channel will be used.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ *
+ * <ul>
+ * <li>CLA = '01' to '03', '40 to 4F'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * In case of UICC it is recommended for the API to reject the opening of the logical
+ * channel without a specific AID, by always answering null to such a request.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2=00.
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @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, byte p2) throws IOException {
+
+ if ((mReader.getName().startsWith("SIM")) && (aid == null)) {
+ Log.e(TAG, "NULL AID not supported on " + mReader.getName());
+ return null;
+ }
+
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openLogicalChannel(
+ aid,
+ p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (RemoteException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 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/android/util/MutableBoolean.java b/core/java/android/util/MutableBoolean.java
index ed837ab..44e73cc 100644
--- a/core/java/android/util/MutableBoolean.java
+++ b/core/java/android/util/MutableBoolean.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableBoolean {
public boolean value;
diff --git a/core/java/android/util/MutableByte.java b/core/java/android/util/MutableByte.java
index cc6b00a..b9ec25d 100644
--- a/core/java/android/util/MutableByte.java
+++ b/core/java/android/util/MutableByte.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableByte {
public byte value;
diff --git a/core/java/android/util/MutableChar.java b/core/java/android/util/MutableChar.java
index 9a2e2bc..9f7a9ae 100644
--- a/core/java/android/util/MutableChar.java
+++ b/core/java/android/util/MutableChar.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableChar {
public char value;
diff --git a/core/java/android/util/MutableDouble.java b/core/java/android/util/MutableDouble.java
index bd7329a..56e539b 100644
--- a/core/java/android/util/MutableDouble.java
+++ b/core/java/android/util/MutableDouble.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableDouble {
public double value;
diff --git a/core/java/android/util/MutableFloat.java b/core/java/android/util/MutableFloat.java
index e6f2d7d..6d7ad59 100644
--- a/core/java/android/util/MutableFloat.java
+++ b/core/java/android/util/MutableFloat.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableFloat {
public float value;
diff --git a/core/java/android/util/MutableInt.java b/core/java/android/util/MutableInt.java
index a3d8606..bb24566 100644
--- a/core/java/android/util/MutableInt.java
+++ b/core/java/android/util/MutableInt.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableInt {
public int value;
diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java
index 575068e..86e70e1 100644
--- a/core/java/android/util/MutableLong.java
+++ b/core/java/android/util/MutableLong.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableLong {
public long value;
diff --git a/core/java/android/util/MutableShort.java b/core/java/android/util/MutableShort.java
index 48fb232..b94ab07 100644
--- a/core/java/android/util/MutableShort.java
+++ b/core/java/android/util/MutableShort.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableShort {
public short value;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 35ab56a..d7f725d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -969,6 +969,12 @@
addOption("--generate-debug-info");
}
+ // The mini-debug-info makes it possible to backtrace through JIT code.
+ if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-mini-debug-info");
+ }
+
/*
* Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will
* contain the fingerprint and can be parsed.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fdda55b..07f9530 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1415,6 +1415,12 @@
android:label="@string/permlab_nfc"
android:protectionLevel="normal" />
+ <!-- Allows applications to receive NFC transaction events.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+ android:protectionLevel="normal" />
+
<!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 8717178..3449a7b 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -57,7 +57,11 @@
// and a handle will be passed to the system process, so no additional permissions
// are required from the system process. Returns NULL if the file can't be opened.
Status addFile(const String16& tag, const string& filename, int flags);
-
+
+ // Create a new Entry from an already opened file. Takes ownership of the
+ // file descriptor.
+ Status addFile(const String16& tag, int fd, int flags);
+
class Entry : public virtual RefBase, public Parcelable {
public:
Entry();
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index bbb45f0..e8e34d7 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -179,7 +179,12 @@
ALOGW("DropboxManager: %s", message.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
}
+ return addFile(tag, fd, flags);
+}
+Status
+DropBoxManager::addFile(const String16& tag, int fd, int flags)
+{
Entry entry(tag, flags, fd);
return add(entry);
}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 184e559..888fedb 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -154,16 +154,16 @@
@AfterClass
public static void enableAnimations() throws Exception {
- if (sWindowAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sWindowAnimationScaleBefore)) {
runShellCommand(
"settings put global window_animation_scale " + sWindowAnimationScaleBefore);
}
- if (sTransitionAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sTransitionAnimationScaleBefore)) {
runShellCommand(
"settings put global transition_animation_scale " +
sTransitionAnimationScaleBefore);
}
- if (sAnimatiorDurationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sAnimatiorDurationScaleBefore)) {
runShellCommand(
"settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 0946181..853cbba 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -129,14 +129,18 @@
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- if (sink.equals(device)) {
- Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
- continue;
+ int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+ if (max_connected_devices == 1) {
+ // Original behavior: disconnect currently connected device
+ List<BluetoothDevice> sinks = getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ if (sink.equals(device)) {
+ Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+ continue;
+ }
+ mService.disconnect(sink);
}
- mService.disconnect(sink);
}
}
return mService.connect(device);
@@ -158,6 +162,16 @@
return mService.getConnectionState(device);
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -181,8 +195,8 @@
boolean isA2dpPlaying() {
if (mService == null) return false;
List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (!sinks.isEmpty()) {
- if (mService.isA2dpPlaying(sinks.get(0))) {
+ for (BluetoothDevice device : sinks) {
+ if (mService.isA2dpPlaying(device)) {
return true;
}
}
@@ -206,8 +220,8 @@
return true;
}
BluetoothCodecConfig codecConfig = null;
- if (mServiceWrapper.getCodecStatus() != null) {
- codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig();
+ if (mServiceWrapper.getCodecStatus(device) != null) {
+ codecConfig = mServiceWrapper.getCodecStatus(device).getCodecConfig();
}
if (codecConfig != null) {
return !codecConfig.isMandatoryCodec();
@@ -225,9 +239,9 @@
return;
}
if (enabled) {
- mService.enableOptionalCodecs();
+ mService.enableOptionalCodecs(device);
} else {
- mService.disableOptionalCodecs();
+ mService.disableOptionalCodecs(device);
}
}
@@ -240,8 +254,8 @@
// We want to get the highest priority codec, since that's the one that will be used with
// this device, and see if it is high-quality (ie non-mandatory).
BluetoothCodecConfig[] selectable = null;
- if (mServiceWrapper.getCodecStatus() != null) {
- selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities();
+ if (mServiceWrapper.getCodecStatus(device) != null) {
+ selectable = mServiceWrapper.getCodecStatus(device).getCodecsSelectableCapabilities();
// To get the highest priority, we sort in reverse.
Arrays.sort(selectable,
(a, b) -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
index aa3e835..dace1bb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
@@ -39,7 +39,7 @@
/**
* Wraps {@code BluetoothA2dp.getCodecStatus}
*/
- public BluetoothCodecStatus getCodecStatus();
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device);
/**
* Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
index 14fa796..c49bb98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
@@ -41,8 +41,8 @@
}
@Override
- public BluetoothCodecStatus getCodecStatus() {
- return mService.getCodecStatus();
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+ return mService.getCodecStatus(device);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b49..ac3599c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+ void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f57d02b..3cda9c9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +34,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -106,6 +110,12 @@
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
+ // Active device broadcasts
+ addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+ addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
@@ -409,4 +419,35 @@
return deviceAdded;
}
+
+ private class ActiveDeviceChangedHandler implements Handler {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+ return;
+ }
+ CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+ int bluetoothProfile = 0;
+ if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.A2DP;
+ } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.HEADSET;
+ } else {
+ Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+ return;
+ }
+ dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+
+ private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff10..fb0f75b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
+ // Active device state
+ private boolean mIsActiveDeviceA2dp = false;
+ private boolean mIsActiveDeviceHeadset = false;
+
/**
* Describes the current device and profile for logging.
*
@@ -156,6 +160,7 @@
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
+ fetchActiveDevices();
}
CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
fetchName();
fetchBtClass();
updateProfiles();
+ fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
return mDevice.getBondState();
}
+ /**
+ * Set the device status as active or non-active per Bluetooth profile.
+ *
+ * @param isActive true if the device is active
+ * @param bluetoothProfile the Bluetooth profile
+ */
+ public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+ boolean changed = false;
+ switch (bluetoothProfile) {
+ case BluetoothProfile.A2DP:
+ changed = (mIsActiveDeviceA2dp != isActive);
+ mIsActiveDeviceA2dp = isActive;
+ break;
+ case BluetoothProfile.HEADSET:
+ changed = (mIsActiveDeviceHeadset != isActive);
+ mIsActiveDeviceHeadset = isActive;
+ break;
+ default:
+ Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+ " isActive " + isActive);
+ break;
+ }
+ if (changed) {
+ dispatchAttributesChanged();
+ }
+ }
+
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
@@ -529,6 +562,17 @@
return true;
}
+ private void fetchActiveDevices() {
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null) {
+ mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+ }
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if (headsetProfile != null) {
+ mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+ }
+ }
+
/**
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
@@ -896,37 +940,60 @@
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
+ // TODO: A temporary workaround solution using string description the device is active.
+ // Issue tracked by b/72317067 .
+ // An alternative solution would be visual indication.
+ // Intentionally not adding the strings to strings.xml for now:
+ // 1) If this is just a short-term solution, no need to waste translation effort
+ // 2) The number of strings with all possible combinations becomes enormously large.
+ // If string description becomes part of the final solution, we MUST NOT
+ // concatenate the strings here: this does not translate well.
+ String activeString = null;
+ if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+ activeString = ", active";
+ } else {
+ if (mIsActiveDeviceA2dp) {
+ activeString = ", active(media)";
+ }
+ if (mIsActiveDeviceHeadset) {
+ activeString = ", active(phone)";
+ }
+ }
+ if (activeString == null) activeString = "";
+
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
+ activeString;
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset);
+ return mContext.getString(R.string.bluetooth_connected_no_headset)
+ + activeString;
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString) + activeString;
} else {
- return mContext.getString(R.string.bluetooth_connected);
+ return mContext.getString(R.string.bluetooth_connected) + activeString;
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d45fe1a..ee12191 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@
return BluetoothProfile.STATE_DISCONNECTED;
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 22674cb..cda4e45 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -239,4 +239,8 @@
public BluetoothDevice getRemoteDevice(String address) {
return mAdapter.getRemoteDevice(address);
}
+
+ public int getMaxConnectedAudioDevices() {
+ return mAdapter.getMaxConnectedAudioDevices();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 4a73c1b..8761807 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -122,7 +122,7 @@
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
when(status.getCodecConfig()).thenReturn(config);
when(config.isMandatoryCodec()).thenReturn(false);
@@ -185,7 +185,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(true);
@@ -200,7 +200,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(false);
diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml
index 43bec91..6843db9 100644
--- a/packages/SystemUI/res/layout/back.xml
+++ b/packages/SystemUI/res/layout/back.xml
@@ -24,8 +24,8 @@
systemui:keyCode="4"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_back"
- android:paddingTop="15dp"
- android:paddingBottom="15dp"
+ android:paddingTop="@dimen/home_padding"
+ android:paddingBottom="@dimen/home_padding"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml
index c84d280..6b08cea 100644
--- a/packages/SystemUI/res/layout/recent_apps.xml
+++ b/packages/SystemUI/res/layout/recent_apps.xml
@@ -23,8 +23,8 @@
android:layout_weight="0"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_recent"
- android:paddingTop="15dp"
- android:paddingBottom="15dp"
+ android:paddingTop="@dimen/home_padding"
+ android:paddingBottom="@dimen/home_padding"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c8ffe8f..a16ea70 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -98,4 +98,7 @@
<!-- The offsets the tasks animate from when recents is launched while docking -->
<dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
+
+ <!-- Home button padding for sizing -->
+ <dimen name="home_padding">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@
public void onScanningStateChanged(boolean started) { }
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) { }
}
private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
old mode 100644
new mode 100755
index e970367..dc2707b
--- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -22,11 +22,15 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
import android.os.IBinder;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
@@ -216,7 +220,27 @@
intent.setClassName(mContext, lastapp.className);
intent.setComponent(new ComponentName(lastapp.packageName,
lastapp.className));
- if (mContext.startService(intent) == null) {
+ PackageManager pm = mContext.getPackageManager();
+ PowerManager powerManager =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ try {
+ ApplicationInfo appInfo = pm.getApplicationInfo(lastapp.packageName, 0);
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.O ||
+ powerManager.isIgnoringBatteryOptimizations(lastapp.packageName)) {
+ if (mContext.startService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ } else {
+ if (mContext.startForegroundService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ }
+
+ } catch (NameNotFoundException e) {
Log.w(LOG_TAG, "invalid name " +
lastapp.packageName + "/" + lastapp.className);
return WapPushManagerParams.INVALID_RECEIVER_NAME;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 7ce0f43..18b00ac 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -90,6 +90,7 @@
"media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
+ "statsd", // Stats daemon
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d3fb087..e944326 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -63,6 +63,7 @@
import static android.os.Process.SCHED_FIFO;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.SCHED_RESET_ON_FORK;
+import static android.os.Process.SE_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SIGNAL_QUIT;
import static android.os.Process.SIGNAL_USR1;
@@ -1493,6 +1494,14 @@
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
+
+ /**
+ * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+ * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+ * A non-null agent in mProfileInfo overrides this.
+ */
+ private @Nullable Map<String, String> mAppAgentMap = null;
+
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -7017,25 +7026,6 @@
}
}
- ProfilerInfo profilerInfo = null;
- String preBindAgent = null;
- if (mProfileApp != null && mProfileApp.equals(processName)) {
- mProfileProc = app;
- if (mProfilerInfo != null) {
- // Send a profiler info object to the app if either a file is given, or
- // an agent should be loaded at bind-time.
- boolean needsInfo = mProfilerInfo.profileFile != null
- || mProfilerInfo.attachAgentDuringBind;
- profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
- if (!mProfilerInfo.attachAgentDuringBind) {
- preBindAgent = mProfilerInfo.agent;
- }
- }
- } else if (app.instr != null && app.instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null, false);
- }
-
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -7060,6 +7050,39 @@
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
+ ProfilerInfo profilerInfo = null;
+ String preBindAgent = null;
+ if (mProfileApp != null && mProfileApp.equals(processName)) {
+ mProfileProc = app;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (mProfilerInfo.agent != null) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
+ } else if (app.instr != null && app.instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
+ }
+ }
+
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
}
@@ -12793,6 +12816,52 @@
}
}
+ /**
+ * Set or remove an agent to be run whenever an app with the given process name starts.
+ *
+ * This method will not check whether the given process name matches a debuggable app. That
+ * would require scanning all current packages, and a rescan when new packages are installed
+ * or updated.
+ *
+ * Instead, do the check when an application is started and matched to a stored agent.
+ *
+ * @param packageName the process name of the app.
+ * @param agent the agent string to be used, or null to remove any previously set agent.
+ */
+ @Override
+ public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(
+ android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (agent == null) {
+ if (mAppAgentMap != null) {
+ mAppAgentMap.remove(packageName);
+ if (mAppAgentMap.isEmpty()) {
+ mAppAgentMap = null;
+ }
+ }
+ } else {
+ if (mAppAgentMap == null) {
+ mAppAgentMap = new HashMap<>();
+ }
+ if (mAppAgentMap.size() >= 100) {
+ // Limit the size of the map, to avoid OOMEs.
+ Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ + "/" + agent);
+ return;
+ }
+ mAppAgentMap.put(packageName, agent);
+ }
+ }
+ }
+
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -19190,6 +19259,7 @@
case PHONE_UID:
case BLUETOOTH_UID:
case NFC_UID:
+ case SE_UID:
isCallerSystem = true;
break;
default:
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a7ace21..59c0ed1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -164,6 +164,8 @@
return runDumpHeap(pw);
case "set-debug-app":
return runSetDebugApp(pw);
+ case "set-agent-app":
+ return runSetAgentApp(pw);
case "clear-debug-app":
return runClearDebugApp(pw);
case "set-watch-heap":
@@ -862,6 +864,13 @@
return 0;
}
+ int runSetAgentApp(PrintWriter pw) throws RemoteException {
+ String pkg = getNextArgRequired();
+ String agent = getNextArg();
+ mInterface.setAgentApp(pkg, agent);
+ return 0;
+ }
+
int runClearDebugApp(PrintWriter pw) throws RemoteException {
mInterface.setDebugApp(null, false, true);
return 0;
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 25b52da..e786bed 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -252,6 +252,29 @@
addWakeupEvent(event);
}
+ @Override
+ public synchronized void onTcpSocketStatsEvent(int[] networkIds,
+ int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
+ if (networkIds.length != sentPackets.length
+ || networkIds.length != lostPackets.length
+ || networkIds.length != rttsUs.length
+ || networkIds.length != sentAckDiffsMs.length) {
+ Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
+ return;
+ }
+
+ long timestamp = System.currentTimeMillis();
+ for (int i = 0; i < networkIds.length; i++) {
+ int netId = networkIds[i];
+ int sent = sentPackets[i];
+ int lost = lostPackets[i];
+ int rttUs = rttsUs[i];
+ int sentAckDiffMs = sentAckDiffsMs[i];
+ getMetricsForNetwork(timestamp, netId)
+ .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
+ }
+ }
+
private void addWakeupEvent(WakeupEvent event) {
String iface = event.iface;
mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index b35ed75..3413291 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IpPrefix;
@@ -476,6 +477,7 @@
ConnectivityManager.getNetworkTypeName(type));
continue;
}
+ nc.setSingleUid(Process.myUid());
for (NetworkState value : netStates) {
if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f182c05..6a53e48 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -426,6 +426,7 @@
private static final int NFC_UID = Process.NFC_UID;
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
+ private static final int SE_UID = Process.SE_UID;
// Cap the size of permission trees that 3rd party apps can define
private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
@@ -2436,6 +2437,8 @@
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.se", SE_UID,
+ ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 7f20c8a..5f1f448 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -40,6 +40,8 @@
private final String mAlphaLong;
// short alpha Operator Name String or Enhanced Operator Name String
private final String mAlphaShort;
+ // cell bandwidth, in kHz
+ private final int mBandwidth;
/**
* @hide
@@ -50,6 +52,7 @@
mPci = Integer.MAX_VALUE;
mTac = Integer.MAX_VALUE;
mEarfcn = Integer.MAX_VALUE;
+ mBandwidth = Integer.MAX_VALUE;
mAlphaLong = null;
mAlphaShort = null;
}
@@ -65,7 +68,8 @@
* @hide
*/
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
- this(ci, pci, tac, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc), null, null);
+ this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+ String.valueOf(mnc), null, null);
}
/**
@@ -80,7 +84,8 @@
* @hide
*/
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
- this(ci, pci, tac, earfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
+ this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+ null, null);
}
/**
@@ -89,6 +94,7 @@
* @param pci Physical Cell Id 0..503
* @param tac 16-bit Tracking Area Code
* @param earfcn 18-bit LTE Absolute RF Channel Number
+ * @param bandwidth cell bandwidth in kHz
* @param mccStr 3-digit Mobile Country Code in string format
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
@@ -96,19 +102,20 @@
*
* @hide
*/
- public CellIdentityLte(int ci, int pci, int tac, int earfcn, String mccStr,
- String mncStr, String alphal, String alphas) {
+ public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
+ String mncStr, String alphal, String alphas) {
super(TAG, TYPE_LTE, mccStr, mncStr);
mCi = ci;
mPci = pci;
mTac = tac;
mEarfcn = earfcn;
+ mBandwidth = bandwidth;
mAlphaLong = alphal;
mAlphaShort = alphas;
}
private CellIdentityLte(CellIdentityLte cid) {
- this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mMccStr,
+ this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
}
@@ -163,6 +170,13 @@
}
/**
+ * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+ */
+ public int getBandwidth() {
+ return mBandwidth;
+ }
+
+ /**
* @return Mobile Country Code in string format, null if unknown
*/
public String getMccStr() {
@@ -219,6 +233,7 @@
&& mPci == o.mPci
&& mTac == o.mTac
&& mEarfcn == o.mEarfcn
+ && mBandwidth == o.mBandwidth
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
&& TextUtils.equals(mAlphaLong, o.mAlphaLong)
@@ -232,6 +247,7 @@
.append(" mPci=").append(mPci)
.append(" mTac=").append(mTac)
.append(" mEarfcn=").append(mEarfcn)
+ .append(" mBandwidth=").append(mBandwidth)
.append(" mMcc=").append(mMccStr)
.append(" mMnc=").append(mMncStr)
.append(" mAlphaLong=").append(mAlphaLong)
@@ -248,6 +264,7 @@
dest.writeInt(mPci);
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
+ dest.writeInt(mBandwidth);
dest.writeString(mAlphaLong);
dest.writeString(mAlphaShort);
}
@@ -259,6 +276,7 @@
mPci = in.readInt();
mTac = in.readInt();
mEarfcn = in.readInt();
+ mBandwidth = in.readInt();
mAlphaLong = in.readString();
mAlphaShort = in.readString();
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b5e4eef..9232ed7 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -16,8 +16,11 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Immutable cell information from a point in time.
@@ -47,6 +50,34 @@
/** @hide */
public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ CONNECTION_NONE,
+ CONNECTION_PRIMARY_SERVING,
+ CONNECTION_SECONDARY_SERVING,
+ CONNECTION_UNKNOWN
+ })
+ public @interface CellConnectionStatus {}
+
+ /**
+ * Cell is not a serving cell.
+ *
+ * <p>The cell has been measured but is neither a camped nor serving cell (3GPP 36.304).
+ */
+ public static final int CONNECTION_NONE = 0;
+
+ /** UE is connected to cell for signalling and possibly data (3GPP 36.331, 25.331). */
+ public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+ /** UE is connected to cell for data (3GPP 36.331, 25.331). */
+ public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+ /** Connection status is unknown. */
+ public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
+ private int mCellConnectionStatus = CONNECTION_NONE;
+
// True if device is mRegistered to the mobile network
private boolean mRegistered;
@@ -69,6 +100,7 @@
this.mRegistered = ci.mRegistered;
this.mTimeStampType = ci.mTimeStampType;
this.mTimeStamp = ci.mTimeStamp;
+ this.mCellConnectionStatus = ci.mCellConnectionStatus;
}
/** True if this cell is registered to the mobile network */
@@ -90,6 +122,25 @@
}
/**
+ * Gets the connection status of this cell.
+ *
+ * @see #CONNECTION_NONE
+ * @see #CONNECTION_PRIMARY_SERVING
+ * @see #CONNECTION_SECONDARY_SERVING
+ * @see #CONNECTION_UNKNOWN
+ *
+ * @return The connection status of the cell.
+ */
+ @CellConnectionStatus
+ public int getCellConnectionStatus() {
+ return mCellConnectionStatus;
+ }
+ /** @hide */
+ public void setCellConnectionStatus(@CellConnectionStatus int cellConnectionStatus) {
+ mCellConnectionStatus = cellConnectionStatus;
+ }
+
+ /**
* Where time stamp gets recorded.
* @return one of TIMESTAMP_TYPE_XXXX
*
@@ -111,7 +162,7 @@
public int hashCode() {
int primeNum = 31;
return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
- + (mTimeStampType * primeNum);
+ + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
}
@Override
@@ -125,7 +176,9 @@
try {
CellInfo o = (CellInfo) other;
return mRegistered == o.mRegistered
- && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+ && mTimeStamp == o.mTimeStamp
+ && mTimeStampType == o.mTimeStampType
+ && mCellConnectionStatus == o.mCellConnectionStatus;
} catch (ClassCastException e) {
return false;
}
@@ -155,6 +208,7 @@
timeStampType = timeStampTypeToString(mTimeStampType);
sb.append(" mTimeStampType=").append(timeStampType);
sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+ sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
return sb.toString();
}
@@ -181,6 +235,7 @@
dest.writeInt(mRegistered ? 1 : 0);
dest.writeInt(mTimeStampType);
dest.writeLong(mTimeStamp);
+ dest.writeInt(mCellConnectionStatus);
}
/**
@@ -192,6 +247,7 @@
mRegistered = (in.readInt() == 1) ? true : false;
mTimeStampType = in.readInt();
mTimeStamp = in.readLong();
+ mCellConnectionStatus = in.readInt();
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
new file mode 100644
index 0000000..97e3037
--- /dev/null
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -0,0 +1,72 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+public class DataSpecificRegistrationStates implements Parcelable{
+ /**
+ * The maximum number of simultaneous Data Calls that
+ * must be established using setupDataCall().
+ */
+ public final int maxDataCalls;
+
+ DataSpecificRegistrationStates(int maxDataCalls) {
+ this.maxDataCalls = maxDataCalls;
+ }
+
+ private DataSpecificRegistrationStates(Parcel source) {
+ maxDataCalls = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(maxDataCalls);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(maxDataCalls);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
+ return false;
+ }
+
+ DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
+ return this.maxDataCalls == other.maxDataCalls;
+ }
+
+ public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
+ new Parcelable.Creator<DataSpecificRegistrationStates>() {
+ @Override
+ public DataSpecificRegistrationStates createFromParcel(Parcel source) {
+ return new DataSpecificRegistrationStates(source);
+ }
+
+ @Override
+ public DataSpecificRegistrationStates[] newArray(int size) {
+ return new DataSpecificRegistrationStates[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/INetworkService.aidl b/telephony/java/android/telephony/INetworkService.aidl
index d810d58..9ef7186 100644
--- a/telephony/java/android/telephony/INetworkService.aidl
+++ b/telephony/java/android/telephony/INetworkService.aidl
@@ -23,7 +23,9 @@
*/
oneway interface INetworkService
{
- void getNetworkRegistrationState(int domain, INetworkServiceCallback callback);
- void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback);
- void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback);
+ void createNetworkServiceProvider(int slotId);
+ void removeNetworkServiceProvider(int slotId);
+ void getNetworkRegistrationState(int slotId, int domain, INetworkServiceCallback callback);
+ void registerForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
+ void unregisterForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index e051069..4f137be 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -105,6 +105,11 @@
@Nullable
private final CellIdentity mCellIdentity;
+ @Nullable
+ private VoiceSpecificRegistrationStates mVoiceSpecificStates;
+
+ @Nullable
+ private DataSpecificRegistrationStates mDataSpecificStates;
/**
* @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
@@ -128,6 +133,34 @@
mEmergencyOnly = emergencyOnly;
}
+ /**
+ * Constructor for voice network registration states.
+ * @hide
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+ int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
+ this(transportType, domain, regState, accessNetworkTechnology,
+ reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+ mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
+ systemIsInPrl, defaultRoamingIndicator);
+ }
+
+ /**
+ * Constructor for data network registration states.
+ * @hide
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+ this(transportType, domain, regState, accessNetworkTechnology,
+ reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+ mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+ }
+
protected NetworkRegistrationState(Parcel source) {
mTransportType = source.readInt();
mDomain = source.readInt();
@@ -137,6 +170,10 @@
mEmergencyOnly = source.readBoolean();
mAvailableServices = source.createIntArray();
mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+ mVoiceSpecificStates = source.readParcelable(
+ VoiceSpecificRegistrationStates.class.getClassLoader());
+ mDataSpecificStates = source.readParcelable(
+ DataSpecificRegistrationStates.class.getClassLoader());
}
/**
@@ -173,6 +210,36 @@
return mAccessNetworkTechnology;
}
+ /**
+ * @return Reason for denial from network.
+ */
+ public int getReasonForDenial() {
+ return mReasonForDenial;
+ }
+
+ /**
+ * @return The cell information.
+ */
+ public CellIdentity getCellIdentity() {
+ return mCellIdentity;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public VoiceSpecificRegistrationStates getVoiceSpecificStates() {
+ return mVoiceSpecificStates;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public DataSpecificRegistrationStates getDataSpecificStates() {
+ return mDataSpecificStates;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -202,13 +269,16 @@
.append(" emergencyEnabled=").append(mEmergencyOnly)
.append(" supportedServices=").append(mAvailableServices)
.append(" cellIdentity=").append(mCellIdentity)
+ .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
+ .append(" dataSpecificStates=").append(mDataSpecificStates)
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
- mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity);
+ mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+ mVoiceSpecificStates, mDataSpecificStates);
}
@Override
@@ -228,7 +298,9 @@
&& mEmergencyOnly == other.mEmergencyOnly
&& (mAvailableServices == other.mAvailableServices
|| Arrays.equals(mAvailableServices, other.mAvailableServices))
- && mCellIdentity == other.mCellIdentity;
+ && mCellIdentity == other.mCellIdentity
+ && mVoiceSpecificStates == other.mVoiceSpecificStates
+ && mDataSpecificStates == other.mDataSpecificStates;
}
@Override
@@ -241,6 +313,8 @@
dest.writeBoolean(mEmergencyOnly);
dest.writeIntArray(mAvailableServices);
dest.writeParcelable(mCellIdentity, 0);
+ dest.writeParcelable(mVoiceSpecificStates, 0);
+ dest.writeParcelable(mDataSpecificStates, 0);
}
public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 6b3584c..94921de 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -53,11 +53,13 @@
public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
- private static final int NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1;
- private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE = 2;
- private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE = 3;
- private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE = 4;
- private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED = 5;
+ private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1;
+ private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2;
+ private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS = 3;
+ private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE = 4;
+ private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE = 5;
+ private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE = 6;
+ private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED = 7;
private final HandlerThread mHandlerThread;
@@ -66,7 +68,7 @@
private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
- private final SparseArray<INetworkServiceWrapper> mBinderMap = new SparseArray<>();
+ private final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
/**
* The abstract class of the actual network service implementation. The network service provider
@@ -147,37 +149,50 @@
public void handleMessage(Message message) {
final int slotId = message.arg1;
final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
- NetworkServiceProvider service;
- synchronized (mServiceMap) {
- service = mServiceMap.get(slotId);
- }
+ NetworkServiceProvider serviceProvider = mServiceMap.get(slotId);
switch (message.what) {
- case NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
- service = createNetworkServiceProvider(message.arg1);
- if (service != null) {
- mServiceMap.put(slotId, service);
+ case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
+ // If the service provider doesn't exist yet, we try to create it.
+ if (serviceProvider == null) {
+ mServiceMap.put(slotId, createNetworkServiceProvider(slotId));
}
break;
+ case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
+ // If the service provider doesn't exist yet, we try to create it.
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ mServiceMap.remove(slotId);
+ }
+ break;
+ case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ serviceProvider = mServiceMap.get(i);
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ break;
case NETWORK_SERVICE_GET_REGISTRATION_STATE:
- if (service == null) break;
+ if (serviceProvider == null) break;
int domainId = message.arg2;
- service.getNetworkRegistrationState(domainId,
+ serviceProvider.getNetworkRegistrationState(domainId,
new NetworkServiceCallback(callback));
break;
case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
- if (service == null) break;
- service.registerForStateChanged(callback);
+ if (serviceProvider == null) break;
+ serviceProvider.registerForStateChanged(callback);
break;
case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
- if (service == null) break;
- service.unregisterForStateChanged(callback);
+ if (serviceProvider == null) break;
+ serviceProvider.unregisterForStateChanged(callback);
break;
case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
- if (service == null) break;
- service.notifyStateChangedToCallbacks();
+ if (serviceProvider == null) break;
+ serviceProvider.notifyStateChangedToCallbacks();
break;
default:
break;
@@ -212,47 +227,14 @@
return null;
}
- int slotId = intent.getIntExtra(
- NETWORK_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
- if (!SubscriptionManager.isValidSlotIndex(slotId)) {
- loge("Invalid slot id " + slotId);
- return null;
- }
-
- log("onBind: slot id=" + slotId);
-
- INetworkServiceWrapper binder = mBinderMap.get(slotId);
- if (binder == null) {
- Message msg = mHandler.obtainMessage(
- NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
- msg.arg1 = slotId;
- msg.sendToTarget();
-
- binder = new INetworkServiceWrapper(slotId);
- mBinderMap.put(slotId, binder);
- }
-
- return binder;
+ return mBinder;
}
/** @hide */
@Override
public boolean onUnbind(Intent intent) {
- int slotId = intent.getIntExtra(NETWORK_SERVICE_EXTRA_SLOT_ID,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- if (mBinderMap.get(slotId) != null) {
- NetworkServiceProvider serviceImpl;
- synchronized (mServiceMap) {
- serviceImpl = mServiceMap.get(slotId);
- }
- // We assume only one component might bind to the service. So if onUnbind is ever
- // called, we destroy the serviceImpl.
- if (serviceImpl != null) {
- serviceImpl.onDestroy();
- }
- mBinderMap.remove(slotId);
- }
+ mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+ 0, null).sendToTarget();
return false;
}
@@ -260,16 +242,6 @@
/** @hide */
@Override
public void onDestroy() {
- synchronized (mServiceMap) {
- for (int i = 0; i < mServiceMap.size(); i++) {
- NetworkServiceProvider serviceImpl = mServiceMap.get(i);
- if (serviceImpl != null) {
- serviceImpl.onDestroy();
- }
- }
- mServiceMap.clear();
- }
-
mHandlerThread.quit();
}
@@ -279,27 +251,36 @@
*/
private class INetworkServiceWrapper extends INetworkService.Stub {
- private final int mSlotId;
-
- INetworkServiceWrapper(int slotId) {
- mSlotId = slotId;
+ @Override
+ public void createNetworkServiceProvider(int slotId) {
+ mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotId,
+ 0, null).sendToTarget();
}
@Override
- public void getNetworkRegistrationState(int domain, INetworkServiceCallback callback) {
- mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, mSlotId,
+ public void removeNetworkServiceProvider(int slotId) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotId,
+ 0, null).sendToTarget();
+ }
+
+ @Override
+ public void getNetworkRegistrationState(
+ int slotId, int domain, INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotId,
domain, callback).sendToTarget();
}
@Override
- public void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
- mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, mSlotId,
+ public void registerForNetworkRegistrationStateChanged(
+ int slotId, INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotId,
0, callback).sendToTarget();
}
@Override
- public void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
- mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, mSlotId,
+ public void unregisterForNetworkRegistrationStateChanged(
+ int slotId,INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotId,
0, callback).sendToTarget();
}
}
@@ -311,4 +292,4 @@
private final void loge(String s) {
Rlog.e(TAG, s);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 77706e8..90a3677 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -25,6 +26,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +38,7 @@
*
* <ul>
* <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ * <li>Duplex mode: UNKNOWN, FDD, TDD
* <li>Roaming indicator
* <li>Operator name, short name and numeric id
* <li>Network selection mode
@@ -71,6 +74,26 @@
*/
public static final int STATE_POWER_OFF = 3;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+ public @interface DuplexMode {}
+
+ /**
+ * Duplex mode for the phone is unknown.
+ */
+ public static final int DUPLEX_MODE_UNKNOWN = 0;
+
+ /**
+ * Duplex mode for the phone is frequency-division duplexing.
+ */
+ public static final int DUPLEX_MODE_FDD = 1;
+
+ /**
+ * Duplex mode for the phone is time-division duplexing.
+ */
+ public static final int DUPLEX_MODE_TDD = 2;
+
/**
* RIL level registration state values from ril.h
* ((const char **)response)[0] is registration state 0-6,
@@ -286,6 +309,9 @@
private boolean mIsUsingCarrierAggregation;
+ private int mChannelNumber;
+ private int[] mCellBandwidths = new int[0];
+
/* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
* Reference: 3GPP TS 36.104 5.4.3 */
private int mLteEarfcnRsrpBoost = 0;
@@ -405,6 +431,8 @@
mLteEarfcnRsrpBoost = in.readInt();
mNetworkRegistrationStates = new ArrayList<>();
in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
+ mChannelNumber = in.readInt();
+ mCellBandwidths = in.createIntArray();
}
public void writeToParcel(Parcel out, int flags) {
@@ -433,6 +461,8 @@
out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
out.writeInt(mLteEarfcnRsrpBoost);
out.writeList(mNetworkRegistrationStates);
+ out.writeInt(mChannelNumber);
+ out.writeIntArray(mCellBandwidths);
}
public int describeContents() {
@@ -486,6 +516,43 @@
}
/**
+ * Get the current duplex mode
+ *
+ * @see #DUPLEX_MODE_UNKNOWN
+ * @see #DUPLEX_MODE_FDD
+ * @see #DUPLEX_MODE_TDD
+ *
+ * @return Current {@code DuplexMode} for the phone
+ */
+ @DuplexMode
+ public int getDuplexMode() {
+ // TODO(b/72117602) determine duplex mode from channel number, using 3GPP 36.101 sections
+ // 5.7.3-1 and 5.5-1
+ return DUPLEX_MODE_UNKNOWN;
+ }
+
+ /**
+ * Get the channel number of the current primary serving cell, or -1 if unknown
+ *
+ * <p>This is EARFCN for LTE, UARFCN for UMTS, and ARFCN for GSM.
+ *
+ * @return Channel number of primary serving cell
+ */
+ public int getChannelNumber() {
+ return mChannelNumber;
+ }
+
+ /**
+ * Get an array of cell bandwidths (kHz) for the current serving cells
+ *
+ * @return Current serving cell bandwidths
+ */
+ @Nullable
+ public int[] getCellBandwidths() {
+ return mCellBandwidths;
+ }
+
+ /**
* Get current roaming indicator of phone
* (note: not just decoding from TS 27.007 7.2)
*
@@ -713,6 +780,8 @@
+ (mDataRegState * 37)
+ mVoiceRoamingType
+ mDataRoamingType
+ + mChannelNumber
+ + Arrays.hashCode(mCellBandwidths)
+ (mIsManualNetworkSelection ? 1 : 0)
+ ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
+ ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
@@ -745,6 +814,8 @@
&& mIsManualNetworkSelection == s.mIsManualNetworkSelection
&& mVoiceRoamingType == s.mVoiceRoamingType
&& mDataRoamingType == s.mDataRoamingType
+ && mChannelNumber == s.mChannelNumber
+ && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
&& equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
&& equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
&& equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
@@ -874,6 +945,8 @@
.append("(" + rilServiceStateToString(mVoiceRegState) + ")")
.append(", mDataRegState=").append(mDataRegState)
.append("(" + rilServiceStateToString(mDataRegState) + ")")
+ .append(", mChannelNumber=").append(mChannelNumber)
+ .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
.append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
.append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
.append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
@@ -905,6 +978,8 @@
mDataRegState = state;
mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+ mChannelNumber = -1;
+ mCellBandwidths = new int[0];
mVoiceOperatorAlphaLong = null;
mVoiceOperatorAlphaShort = null;
mVoiceOperatorNumeric = null;
@@ -953,6 +1028,16 @@
if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
}
+ /** @hide */
+ public void setCellBandwidths(int[] bandwidths) {
+ mCellBandwidths = bandwidths;
+ }
+
+ /** @hide */
+ public void setChannelNumber(int channelNumber) {
+ mChannelNumber = channelNumber;
+ }
+
public void setRoaming(boolean roaming) {
mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
mDataRoamingType = mVoiceRoamingType;
@@ -1101,6 +1186,8 @@
mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+ mChannelNumber = m.getInt("ChannelNumber");
+ mCellBandwidths = m.getIntArray("CellBandwidths");
}
/**
@@ -1132,6 +1219,8 @@
m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+ m.putInt("ChannelNumber", mChannelNumber);
+ m.putIntArray("CellBandwidths", mCellBandwidths);
}
/** @hide */
diff --git a/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
new file mode 100644
index 0000000..871ee4d
--- /dev/null
+++ b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
@@ -0,0 +1,114 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationStates implements Parcelable{
+ /**
+ * oncurrent services support indicator. if
+ * registered on a CDMA system.
+ * false - Concurrent services not supported,
+ * true - Concurrent services supported
+ */
+ public final boolean cssSupported;
+
+ /**
+ * TSB-58 Roaming Indicator if registered
+ * on a CDMA or EVDO system or -1 if not.
+ * Valid values are 0-255.
+ */
+ public final int roamingIndicator;
+
+ /**
+ * indicates whether the current system is in the
+ * PRL if registered on a CDMA or EVDO system or -1 if
+ * not. 0=not in the PRL, 1=in the PRL
+ */
+ public final int systemIsInPrl;
+
+ /**
+ * default Roaming Indicator from the PRL,
+ * if registered on a CDMA or EVDO system or -1 if not.
+ * Valid values are 0-255.
+ */
+ public final int defaultRoamingIndicator;
+
+ VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+ int defaultRoamingIndicator) {
+ this.cssSupported = cssSupported;
+ this.roamingIndicator = roamingIndicator;
+ this.systemIsInPrl = systemIsInPrl;
+ this.defaultRoamingIndicator = defaultRoamingIndicator;
+ }
+
+ private VoiceSpecificRegistrationStates(Parcel source) {
+ this.cssSupported = source.readBoolean();
+ this.roamingIndicator = source.readInt();
+ this.systemIsInPrl = source.readInt();
+ this.defaultRoamingIndicator = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(cssSupported);
+ dest.writeInt(roamingIndicator);
+ dest.writeInt(systemIsInPrl);
+ dest.writeInt(defaultRoamingIndicator);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "VoiceSpecificRegistrationStates {"
+ + " mCssSupported=" + cssSupported
+ + " mRoamingIndicator=" + roamingIndicator
+ + " mSystemIsInPrl=" + systemIsInPrl
+ + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof VoiceSpecificRegistrationStates)) {
+ return false;
+ }
+
+ VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o;
+ return this.cssSupported == other.cssSupported
+ && this.roamingIndicator == other.roamingIndicator
+ && this.systemIsInPrl == other.systemIsInPrl
+ && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+ }
+
+
+ public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR =
+ new Parcelable.Creator<VoiceSpecificRegistrationStates>() {
+ @Override
+ public VoiceSpecificRegistrationStates createFromParcel(Parcel source) {
+ return new VoiceSpecificRegistrationStates(source);
+ }
+
+ @Override
+ public VoiceSpecificRegistrationStates[] newArray(int size) {
+ return new VoiceSpecificRegistrationStates[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index fa19ea0..63e8c3b 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -18,6 +18,8 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -30,7 +32,6 @@
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants;
import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
import android.util.SparseArray;
import java.lang.annotation.Retention;
@@ -86,15 +87,17 @@
/** The reason of the data request is IWLAN handover */
public static final int REQUEST_REASON_HANDOVER = 3;
- private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1;
- private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 2;
- private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 3;
- private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 4;
- private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 5;
- private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 6;
- private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 7;
- private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 8;
- private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 9;
+ private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER = 1;
+ private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER = 2;
+ private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS = 3;
+ private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 4;
+ private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 5;
+ private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 6;
+ private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 7;
+ private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 8;
+ private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 9;
+ private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 10;
+ private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11;
private final HandlerThread mHandlerThread;
@@ -102,7 +105,7 @@
private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
- private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+ private final IBinder mBinder = new IDataServiceWrapper();
/**
* The abstract class of the actual data service implementation. The data service provider
@@ -136,19 +139,21 @@
* the provided callback to notify the platform.
*
* @param accessNetworkType Access network type that the data call will be established on.
- * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+ * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
* @param dataProfile Data profile used for data call setup. See {@link DataProfile}
* @param isRoaming True if the device is data roaming.
* @param allowRoaming True if data roaming is allowed by the user.
* @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
- * {@link #REQUEST_REASON_HANDOVER}.
+ * {@link #REQUEST_REASON_HANDOVER}.
* @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
- * link properties of the existing data connection, otherwise null.
- * @param callback The result callback for this request.
+ * link properties of the existing data connection, otherwise null.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
*/
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, @SetupDataReason int reason,
- LinkProperties linkProperties, DataServiceCallback callback) {
+ @Nullable LinkProperties linkProperties,
+ @Nullable DataServiceCallback callback) {
// The default implementation is to return unsupported.
callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
}
@@ -159,13 +164,15 @@
* provided callback to notify the platform.
*
* @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
- * int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+ * int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
* @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
- * {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
- * @param callback The result callback for this request.
+ * {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
+ *
*/
public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
- DataServiceCallback callback) {
+ @Nullable DataServiceCallback callback) {
// The default implementation is to return unsupported.
callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
@@ -175,10 +182,11 @@
*
* @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
* @param isRoaming True if the device is data roaming.
- * @param callback The result callback for this request.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
*/
public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
- DataServiceCallback callback) {
+ @Nullable DataServiceCallback callback) {
// The default implementation is to return unsupported.
callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
@@ -190,10 +198,11 @@
*
* @param dps A list of data profiles.
* @param isRoaming True if the device is data roaming.
- * @param callback The result callback for this request.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
*/
public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
- DataServiceCallback callback) {
+ @Nullable DataServiceCallback callback) {
// The default implementation is to return unsupported.
callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
}
@@ -203,7 +212,7 @@
*
* @param callback The result callback for this request.
*/
- public void getDataCallList(DataServiceCallback callback) {
+ public void getDataCallList(@NonNull DataServiceCallback callback) {
// The default implementation is to return unsupported.
callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
}
@@ -321,70 +330,89 @@
public void handleMessage(Message message) {
IDataServiceCallback callback;
final int slotId = message.arg1;
- DataServiceProvider service;
-
- synchronized (mServiceMap) {
- service = mServiceMap.get(slotId);
- }
+ DataServiceProvider serviceProvider = mServiceMap.get(slotId);
switch (message.what) {
- case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
- service = createDataServiceProvider(message.arg1);
- if (service != null) {
- mServiceMap.put(slotId, service);
+ case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+ serviceProvider = createDataServiceProvider(message.arg1);
+ if (serviceProvider != null) {
+ mServiceMap.put(slotId, serviceProvider);
}
break;
+ case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ mServiceMap.remove(slotId);
+ }
+ break;
+ case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ serviceProvider = mServiceMap.get(i);
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ break;
case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
- if (service == null) break;
+ if (serviceProvider == null) break;
SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
- service.setupDataCall(setupDataCallRequest.accessNetworkType,
+ serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
setupDataCallRequest.linkProperties,
- new DataServiceCallback(setupDataCallRequest.callback));
+ (setupDataCallRequest.callback != null)
+ ? new DataServiceCallback(setupDataCallRequest.callback)
+ : null);
break;
case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
- if (service == null) break;
+ if (serviceProvider == null) break;
DeactivateDataCallRequest deactivateDataCallRequest =
(DeactivateDataCallRequest) message.obj;
- service.deactivateDataCall(deactivateDataCallRequest.cid,
+ serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
deactivateDataCallRequest.reason,
- new DataServiceCallback(deactivateDataCallRequest.callback));
+ (deactivateDataCallRequest.callback != null)
+ ? new DataServiceCallback(deactivateDataCallRequest.callback)
+ : null);
break;
case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
- if (service == null) break;
+ if (serviceProvider == null) break;
SetInitialAttachApnRequest setInitialAttachApnRequest =
(SetInitialAttachApnRequest) message.obj;
- service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+ serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
setInitialAttachApnRequest.isRoaming,
- new DataServiceCallback(setInitialAttachApnRequest.callback));
+ (setInitialAttachApnRequest.callback != null)
+ ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+ : null);
break;
case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
- if (service == null) break;
+ if (serviceProvider == null) break;
SetDataProfileRequest setDataProfileRequest =
(SetDataProfileRequest) message.obj;
- service.setDataProfile(setDataProfileRequest.dps,
+ serviceProvider.setDataProfile(setDataProfileRequest.dps,
setDataProfileRequest.isRoaming,
- new DataServiceCallback(setDataProfileRequest.callback));
+ (setDataProfileRequest.callback != null)
+ ? new DataServiceCallback(setDataProfileRequest.callback)
+ : null);
break;
case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
- if (service == null) break;
+ if (serviceProvider == null) break;
- service.getDataCallList(new DataServiceCallback(
+ serviceProvider.getDataCallList(new DataServiceCallback(
(IDataServiceCallback) message.obj));
break;
case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
- if (service == null) break;
- service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+ if (serviceProvider == null) break;
+ serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
break;
case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
- if (service == null) break;
+ if (serviceProvider == null) break;
callback = (IDataServiceCallback) message.obj;
- service.unregisterForDataCallListChanged(callback);
+ serviceProvider.unregisterForDataCallListChanged(callback);
break;
case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
- if (service == null) break;
+ if (serviceProvider == null) break;
DataCallListChangedIndication indication =
(DataCallListChangedIndication) message.obj;
try {
@@ -423,67 +451,19 @@
loge("Unexpected intent " + intent);
return null;
}
-
- int slotId = intent.getIntExtra(
- DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
- if (!SubscriptionManager.isValidSlotIndex(slotId)) {
- loge("Invalid slot id " + slotId);
- return null;
- }
-
- log("onBind: slot id=" + slotId);
-
- IDataServiceWrapper binder = mBinderMap.get(slotId);
- if (binder == null) {
- Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
- msg.arg1 = slotId;
- msg.sendToTarget();
-
- binder = new IDataServiceWrapper(slotId);
- mBinderMap.put(slotId, binder);
- }
-
- return binder;
+ return mBinder;
}
/** @hide */
@Override
public boolean onUnbind(Intent intent) {
- int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- if (mBinderMap.get(slotId) != null) {
- DataServiceProvider serviceImpl;
- synchronized (mServiceMap) {
- serviceImpl = mServiceMap.get(slotId);
- }
- if (serviceImpl != null) {
- serviceImpl.onDestroy();
- }
- mBinderMap.remove(slotId);
- }
-
- // If all clients unbinds, quit the handler thread
- if (mBinderMap.size() == 0) {
- mHandlerThread.quit();
- }
-
+ mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
return false;
}
/** @hide */
@Override
public void onDestroy() {
- synchronized (mServiceMap) {
- for (int i = 0; i < mServiceMap.size(); i++) {
- DataServiceProvider serviceImpl = mServiceMap.get(i);
- if (serviceImpl != null) {
- serviceImpl.onDestroy();
- }
- }
- mServiceMap.clear();
- }
-
mHandlerThread.quit();
}
@@ -491,68 +471,78 @@
* A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
*/
private class IDataServiceWrapper extends IDataService.Stub {
-
- private final int mSlotId;
-
- IDataServiceWrapper(int slotId) {
- mSlotId = slotId;
+ @Override
+ public void createDataServiceProvider(int slotId) {
+ mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
+ .sendToTarget();
}
@Override
- public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+ public void removeDataServiceProvider(int slotId) {
+ mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming, int reason,
LinkProperties linkProperties, IDataServiceCallback callback) {
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
allowRoaming, reason, linkProperties, callback))
.sendToTarget();
}
@Override
- public void deactivateDataCall(int cid, int reason, IDataServiceCallback callback) {
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+ public void deactivateDataCall(int slotId, int cid, int reason,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
new DeactivateDataCallRequest(cid, reason, callback))
.sendToTarget();
}
@Override
- public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
IDataServiceCallback callback) {
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
.sendToTarget();
}
@Override
- public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
IDataServiceCallback callback) {
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
}
@Override
- public void getDataCallList(IDataServiceCallback callback) {
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+ public void getDataCallList(int slotId, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("getDataCallList: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
callback).sendToTarget();
}
@Override
- public void registerForDataCallListChanged(IDataServiceCallback callback) {
+ public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
if (callback == null) {
- loge("Callback is null");
+ loge("registerForDataCallListChanged: callback is null");
return;
}
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
0, callback).sendToTarget();
}
@Override
- public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
if (callback == null) {
- loge("Callback is null");
+ loge("unregisterForDataCallListChanged: callback is null");
return;
}
- mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
0, callback).sendToTarget();
}
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index b6a81f9..4af31b5 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.net.LinkProperties;
import android.os.RemoteException;
import android.telephony.Rlog;
import android.telephony.data.DataService.DataServiceProvider;
@@ -37,7 +38,7 @@
@SystemApi
public class DataServiceCallback {
- private static final String mTag = DataServiceCallback.class.getSimpleName();
+ private static final String TAG = DataServiceCallback.class.getSimpleName();
/**
* Result of data requests
@@ -46,7 +47,7 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
RESULT_ERROR_ILLEGAL_STATE})
- public @interface Result {}
+ public @interface ResultCode {}
/** Request is completed successfully */
public static final int RESULT_SUCCESS = 0;
@@ -68,35 +69,35 @@
/**
* Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
- * DataProfile, boolean, boolean, boolean, DataServiceCallback)}.
+ * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
*
- * @param result The result code. Must be one of the {@link Result}.
+ * @param result The result code. Must be one of the {@link ResultCode}.
* @param response Setup data call response.
*/
- public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+ public void onSetupDataCallComplete(@ResultCode int result, DataCallResponse response) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
try {
callback.onSetupDataCallComplete(result, response);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+ Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
}
}
}
/**
* Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
- * boolean, boolean, DataServiceCallback)}.
+ * int, DataServiceCallback)}
*
- * @param result The result code. Must be one of the {@link Result}.
+ * @param result The result code. Must be one of the {@link ResultCode}.
*/
- public void onDeactivateDataCallComplete(@Result int result) {
+ public void onDeactivateDataCallComplete(@ResultCode int result) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
try {
callback.onDeactivateDataCallComplete(result);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote");
+ Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
}
}
}
@@ -105,15 +106,15 @@
* Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
* DataProfile, boolean, DataServiceCallback)}.
*
- * @param result The result code. Must be one of the {@link Result}.
+ * @param result The result code. Must be one of the {@link ResultCode}.
*/
- public void onSetInitialAttachApnComplete(@Result int result) {
+ public void onSetInitialAttachApnComplete(@ResultCode int result) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
try {
callback.onSetInitialAttachApnComplete(result);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote");
+ Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
}
}
}
@@ -122,16 +123,16 @@
* Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
* boolean, DataServiceCallback)}.
*
- * @param result The result code. Must be one of the {@link Result}.
+ * @param result The result code. Must be one of the {@link ResultCode}.
*/
@SystemApi
- public void onSetDataProfileComplete(@Result int result) {
+ public void onSetDataProfileComplete(@ResultCode int result) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
try {
callback.onSetDataProfileComplete(result);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote");
+ Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
}
}
}
@@ -140,16 +141,17 @@
* Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
* DataServiceCallback)}.
*
- * @param result The result code. Must be one of the {@link Result}.
+ * @param result The result code. Must be one of the {@link ResultCode}.
* @param dataCallList List of the current active data connection.
*/
- public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+ public void onGetDataCallListComplete(@ResultCode int result,
+ List<DataCallResponse> dataCallList) {
IDataServiceCallback callback = mCallback.get();
if (callback != null) {
try {
callback.onGetDataCallListComplete(result, dataCallList);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote");
+ Rlog.e(TAG, "Failed to onGetDataCallListComplete on the remote");
}
}
}
@@ -165,7 +167,7 @@
try {
callback.onDataCallListChanged(dataCallList);
} catch (RemoteException e) {
- Rlog.e(mTag, "Failed to onDataCallListChanged on the remote");
+ Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
}
}
}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 07720b6..d4d9be8 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -25,14 +25,17 @@
*/
oneway interface IDataService
{
- void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+ void createDataServiceProvider(int slotId);
+ void removeDataServiceProvider(int slotId);
+ void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, in LinkProperties linkProperties,
IDataServiceCallback callback);
- void deactivateDataCall(int cid, int reason, IDataServiceCallback callback);
- void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming,
+ void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
+ void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
IDataServiceCallback callback);
- void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback);
- void getDataCallList(IDataServiceCallback callback);
- void registerForDataCallListChanged(IDataServiceCallback callback);
- void unregisterForDataCallListChanged(IDataServiceCallback callback);
+ void setDataProfile(int slotId, in List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback);
+ void getDataCallList(int slotId, IDataServiceCallback callback);
+ void registerForDataCallListChanged(int slotId, IDataServiceCallback callback);
+ void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback);
}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 88bae33..a1a6a5a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -110,6 +110,9 @@
/** Result code of execution with no error. */
public static final int RESULT_OK = 0;
+ /** Result code of an unknown error. */
+ public static final int RESULT_UNKNOWN_ERROR = -1;
+
/**
* Callback to receive the result of an eUICC card API.
*
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 8bb709f..662056e 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -276,8 +276,8 @@
*
* @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
* {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+ * TODO(b/35851809): Make this a SystemApi.
*/
- @SystemApi
public int getOtaStatus() {
if (!isEnabled()) {
return EUICC_OTA_STATUS_UNAVAILABLE;