Merge "Set DEXOPT_DISABLE_HIDDEN_API_CHECKS for system apps"
diff --git a/Android.bp b/Android.bp
index 4d78112..6712461 100644
--- a/Android.bp
+++ b/Android.bp
@@ -473,6 +473,8 @@
"telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+ "telephony/java/android/telephony/INetworkService.aidl",
+ "telephony/java/android/telephony/INetworkServiceCallback.aidl",
"telephony/java/com/android/ims/internal/IImsCallSession.aidl",
"telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
"telephony/java/com/android/ims/internal/IImsConfig.aidl",
diff --git a/api/current.txt b/api/current.txt
index a1e60e5..7954bf5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -39890,6 +39890,7 @@
field public static final int EUTRAN = 3; // 0x3
field public static final int GERAN = 1; // 0x1
field public static final int IWLAN = 5; // 0x5
+ field public static final int UNKNOWN = 0; // 0x0
field public static final int UTRAN = 2; // 0x2
}
diff --git a/api/system-current.txt b/api/system-current.txt
index e0f8ecf..1dd7c6d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3967,6 +3967,63 @@
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public class NetworkRegistrationState implements android.os.Parcelable {
+ ctor public NetworkRegistrationState(int, int, int, int, int, boolean, int[], android.telephony.CellIdentity);
+ ctor protected NetworkRegistrationState(android.os.Parcel);
+ method public int describeContents();
+ method public int getAccessNetworkTechnology();
+ method public int[] getAvailableServices();
+ method public int getDomain();
+ method public int getRegState();
+ method public int getTransportType();
+ method public boolean isEmergencyEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR;
+ field public static final int DOMAIN_CS = 1; // 0x1
+ field public static final int DOMAIN_PS = 2; // 0x2
+ field public static final int REG_STATE_DENIED = 3; // 0x3
+ field public static final int REG_STATE_HOME = 1; // 0x1
+ field public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0; // 0x0
+ field public static final int REG_STATE_NOT_REG_SEARCHING = 2; // 0x2
+ field public static final int REG_STATE_ROAMING = 5; // 0x5
+ field public static final int REG_STATE_UNKNOWN = 4; // 0x4
+ field public static final int SERVICE_TYPE_DATA = 2; // 0x2
+ field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5
+ field public static final int SERVICE_TYPE_SMS = 3; // 0x3
+ field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4
+ field public static final int SERVICE_TYPE_VOICE = 1; // 0x1
+ }
+
+ public abstract class NetworkService extends android.app.Service {
+ method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
+ field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
+ field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
+ }
+
+ public class NetworkService.NetworkServiceProvider {
+ ctor public NetworkService.NetworkServiceProvider(int);
+ method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
+ method public final int getSlotId();
+ method public final void notifyNetworkRegistrationStateChanged();
+ method protected void onDestroy();
+ }
+
+ public class NetworkServiceCallback {
+ method public void onGetNetworkRegistrationStateComplete(int, android.telephony.NetworkRegistrationState);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_FAILED = 5; // 0x5
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public class ServiceState implements android.os.Parcelable {
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+ method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ }
+
public final class SmsManager {
method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index c2adbfa..fe4ac6d7 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -65,7 +65,6 @@
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.List;
import libcore.io.IoUtils;
@@ -596,6 +595,10 @@
return mSpi;
}
+ public EncapSocketRecord getSocketRecord() {
+ return mSocket;
+ }
+
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
@@ -806,9 +809,29 @@
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
- // TODO: Add calls to netd
+ // Calls to netd
// Teardown VTI
// Delete global policies
+ try {
+ mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
+
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityPolicy(
+ 0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff);
+ }
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (RemoteException e) {
+ Log.e(
+ TAG,
+ "Failed to delete VTI with interface name: "
+ + mInterfaceName
+ + " and id: "
+ + mResourceId);
+ }
getResourceTracker().give();
releaseNetId(mIkey);
@@ -1229,24 +1252,57 @@
final int okey = reserveNetId();
String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
- // TODO: Add calls to netd:
- // Create VTI
- // Add inbound/outbound global policies
- // (use reqid = 0)
+ try {
+ // Calls to netd:
+ // Create VTI
+ // Add inbound/outbound global policies
+ // (use reqid = 0)
+ mSrvConfig
+ .getNetdInstance()
+ .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
- userRecord.mTunnelInterfaceRecords.put(
- resourceId,
- new RefcountedResource<TunnelInterfaceRecord>(
- new TunnelInterfaceRecord(
- resourceId,
- intfName,
- underlyingNetwork,
- localAddr,
- remoteAddr,
- ikey,
- okey),
- binder));
- return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityPolicy(
+ 0, // Use 0 for reqId
+ direction,
+ "",
+ "",
+ 0,
+ mark,
+ 0xffffffff);
+ }
+
+ userRecord.mTunnelInterfaceRecords.put(
+ resourceId,
+ new RefcountedResource<TunnelInterfaceRecord>(
+ new TunnelInterfaceRecord(
+ resourceId,
+ intfName,
+ underlyingNetwork,
+ localAddr,
+ remoteAddr,
+ ikey,
+ okey),
+ binder));
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+ } catch (RemoteException e) {
+ // Release keys if we got an error.
+ releaseNetId(ikey);
+ releaseNetId(okey);
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ }
+
+ // If we make it to here, then something has gone wrong and we couldn't create a VTI.
+ // Release the keys that we reserved, and return an error status.
+ releaseNetId(ikey);
+ releaseNetId(okey);
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
/**
@@ -1381,12 +1437,50 @@
}
}
+ private void createOrUpdateTransform(
+ IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
+ throws RemoteException {
+
+ int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
+ if (encapType != IpSecTransform.ENCAP_NONE) {
+ encapLocalPort = socketRecord.getPort();
+ encapRemotePort = c.getEncapRemotePort();
+ }
+
+ IpSecAlgorithm auth = c.getAuthentication();
+ IpSecAlgorithm crypt = c.getEncryption();
+ IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
+
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityAssociation(
+ resourceId,
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+ spiRecord.getSpi(),
+ c.getMarkValue(),
+ c.getMarkMask(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ (crypt != null) ? crypt.getName() : "",
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort);
+ }
+
/**
- * Create a transport mode transform, which represent two security associations (one in each
- * direction) in the kernel. The transform will be cached by the system server and must be freed
- * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
- * that are using it, which will result in all of those sockets becoming unable to send or
- * receive data.
+ * Create a IPsec transform, which represents a single security association in the kernel. The
+ * transform will be cached by the system server and must be freed when no longer needed. It is
+ * possible to free one, deleting the SA from underneath sockets that are using it, which will
+ * result in all of those sockets becoming unable to send or receive data.
*/
@Override
public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
@@ -1402,58 +1496,28 @@
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- int encapType, encapLocalPort = 0, encapRemotePort = 0;
EncapSocketRecord socketRecord = null;
- encapType = c.getEncapType();
- if (encapType != IpSecTransform.ENCAP_NONE) {
+ if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
c.getEncapSocketResourceId());
dependencies.add(refcountedSocketRecord);
-
socketRecord = refcountedSocketRecord.getResource();
- encapLocalPort = socketRecord.getPort();
- encapRemotePort = c.getEncapRemotePort();
}
- IpSecAlgorithm auth = c.getAuthentication();
- IpSecAlgorithm crypt = c.getEncryption();
- IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
-
RefcountedResource<SpiRecord> refcountedSpiRecord =
userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
dependencies.add(refcountedSpiRecord);
SpiRecord spiRecord = refcountedSpiRecord.getResource();
try {
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- resourceId,
- c.getMode(),
- c.getSourceAddress(),
- c.getDestinationAddress(),
- (c.getNetwork() != null) ? c.getNetwork().netId : 0,
- spiRecord.getSpi(),
- c.getMarkValue(),
- c.getMarkMask(),
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- (crypt != null) ? crypt.getName() : "",
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort);
+ createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- // Both SAs were created successfully, time to construct a record and lock it away
+
+ // SA was created successfully, time to construct a record and lock it away
userRecord.mTransformRecords.put(
resourceId,
new RefcountedResource<TransformRecord>(
@@ -1561,14 +1625,48 @@
c.getMode() == IpSecTransform.MODE_TUNNEL,
"Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
+ EncapSocketRecord socketRecord = null;
+ if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+ socketRecord =
+ userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
+ }
+ SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
+
int mark =
(direction == IpSecManager.DIRECTION_IN)
? tunnelInterfaceInfo.getIkey()
: tunnelInterfaceInfo.getOkey();
- // TODO: Add calls to netd:
- // Update SA with tunnel mark (ikey or okey based on direction)
- // If outbound, add SPI to policy
+ try {
+ c.setMarkValue(mark);
+ c.setMarkMask(0xffffffff);
+
+ if (direction == IpSecManager.DIRECTION_OUT) {
+ // Set output mark via underlying network (output only)
+ c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
+
+ // If outbound, also add SPI to the policy.
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecUpdateSecurityPolicy(
+ 0, // Use 0 for reqId
+ direction,
+ "",
+ "",
+ transformInfo.getSpiRecord().getSpi(),
+ mark,
+ 0xffffffff);
+ }
+
+ // Update SA with tunnel mark (ikey or okey based on direction)
+ createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == EINVAL) {
+ throw new IllegalArgumentException(e.toString());
+ } else {
+ throw e;
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f54b243..4293d45 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -136,7 +136,6 @@
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -768,7 +767,7 @@
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
- intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2929,14 +2928,28 @@
}
public void setBluetoothScoOnInt(boolean on, String eventSource) {
+ if (DEBUG_DEVICES) {
+ Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
+ }
if (on) {
// do not accept SCO ON if SCO audio is not connected
- synchronized(mScoClients) {
- if ((mBluetoothHeadset != null) &&
- (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
- != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
- mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
- return;
+ synchronized (mScoClients) {
+ if (mBluetoothHeadset != null) {
+ if (mBluetoothHeadsetDevice == null) {
+ BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
+ if (activeDevice != null) {
+ // setBtScoActiveDevice() might trigger resetBluetoothSco() which
+ // will call setBluetoothScoOnInt(false, "resetBluetoothSco")
+ setBtScoActiveDevice(activeDevice);
+ }
+ }
+ if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
+ + mBluetoothHeadsetDevice + " is not in audio connected mode");
+ return;
+ }
}
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
@@ -3324,24 +3337,23 @@
}
}
- void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
+ private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
if (btDevice == null) {
- return;
+ return true;
}
-
String address = btDevice.getAddress();
BluetoothClass btClass = btDevice.getBluetoothClass();
int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
if (btClass != null) {
switch (btClass.getDeviceClass()) {
- case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
- case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- break;
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- break;
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ break;
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ break;
}
}
@@ -3349,34 +3361,33 @@
address = "";
}
- boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-
String btDeviceName = btDevice.getName();
- boolean success =
- handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
- handleDeviceConnection(connected, inDevice, address, btDeviceName);
+ boolean result = handleDeviceConnection(isActive, outDevice, address, btDeviceName);
+ // handleDeviceConnection() && result to make sure the method get executed
+ result = handleDeviceConnection(isActive, inDevice, address, btDeviceName) && result;
+ return result;
+ }
- if (!success) {
- return;
+ void setBtScoActiveDevice(BluetoothDevice btDevice) {
+ if (DEBUG_DEVICES) {
+ Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
}
-
- /* When one BT headset is disconnected while another BT headset
- * is connected, don't mess with the headset device.
- */
- if ((state == BluetoothProfile.STATE_DISCONNECTED ||
- state == BluetoothProfile.STATE_DISCONNECTING) &&
- mBluetoothHeadset != null &&
- mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- Log.w(TAG, "SCO connected through another device, returning");
- return;
- }
-
synchronized (mScoClients) {
- if (connected) {
+ final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
+ if (!Objects.equals(btDevice, previousActiveDevice)) {
+ if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
+ Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
+ + previousActiveDevice);
+ }
+ if (!handleBtScoActiveDeviceChange(btDevice, true)) {
+ Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
+ // set mBluetoothHeadsetDevice to null when failing to add new device
+ btDevice = null;
+ }
mBluetoothHeadsetDevice = btDevice;
- } else {
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
+ if (mBluetoothHeadsetDevice == null) {
+ resetBluetoothSco();
+ }
}
}
}
@@ -3431,12 +3442,7 @@
// Discard timeout message
mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
mBluetoothHeadset = (BluetoothHeadset) proxy;
- deviceList = mBluetoothHeadset.getConnectedDevices();
- if (deviceList.size() > 0) {
- mBluetoothHeadsetDevice = deviceList.get(0);
- } else {
- mBluetoothHeadsetDevice = null;
- }
+ setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
// Refresh SCO audio state
checkScoAudioState();
// Continue pending action if any
@@ -3557,10 +3563,7 @@
void disconnectHeadset() {
synchronized (mScoClients) {
- if (mBluetoothHeadsetDevice != null) {
- setBtScoDeviceConnectionState(mBluetoothHeadsetDevice,
- BluetoothProfile.STATE_DISCONNECTED);
- }
+ setBtScoActiveDevice(null);
mBluetoothHeadset = null;
}
}
@@ -5732,11 +5735,9 @@
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
}
mDockState = dockState;
- } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
- state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
- BluetoothProfile.STATE_DISCONNECTED);
+ } else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- setBtScoDeviceConnectionState(btDevice, state);
+ setBtScoActiveDevice(btDevice);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 703f96d..7cd16128 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -24,6 +24,7 @@
public final class AccessNetworkConstants {
public static final class AccessNetworkType {
+ public static final int UNKNOWN = 0;
public static final int GERAN = 1;
public static final int UTRAN = 2;
public static final int EUTRAN = 3;
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 56e1e64..4fa304a 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -310,6 +310,13 @@
* {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
+
+ /**
+ * The network has reported that an alternative emergency number has been dialed, but the user
+ * must exit airplane mode to place the call.
+ */
+ public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -462,6 +469,8 @@
return "EMERGENCY_PERM_FAILURE";
case NORMAL_UNSPECIFIED:
return "NORMAL_UNSPECIFIED";
+ case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
+ return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/INetworkService.aidl b/telephony/java/android/telephony/INetworkService.aidl
new file mode 100644
index 0000000..d810d58
--- /dev/null
+++ b/telephony/java/android/telephony/INetworkService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.telephony.INetworkServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface INetworkService
+{
+ void getNetworkRegistrationState(int domain, INetworkServiceCallback callback);
+ void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback);
+ void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/INetworkServiceCallback.aidl b/telephony/java/android/telephony/INetworkServiceCallback.aidl
new file mode 100644
index 0000000..520598f
--- /dev/null
+++ b/telephony/java/android/telephony/INetworkServiceCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.telephony.NetworkRegistrationState;
+
+/**
+ * Network service call back interface
+ * @hide
+ */
+oneway interface INetworkServiceCallback
+{
+ void onGetNetworkRegistrationStateComplete(int result, in NetworkRegistrationState state);
+ void onNetworkStateChanged();
+}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.aidl b/telephony/java/android/telephony/NetworkRegistrationState.aidl
new file mode 100644
index 0000000..98cba77
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkRegistrationState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable NetworkRegistrationState;
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
new file mode 100644
index 0000000..e051069
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Description of a mobile network registration state
+ * @hide
+ */
+@SystemApi
+public class NetworkRegistrationState implements Parcelable {
+ /**
+ * Network domain
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DOMAIN_", value = {DOMAIN_CS, DOMAIN_PS})
+ public @interface Domain {}
+
+ /** Circuit switching domain */
+ public static final int DOMAIN_CS = 1;
+ /** Packet switching domain */
+ public static final int DOMAIN_PS = 2;
+
+ /**
+ * Registration state
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REG_STATE_",
+ value = {REG_STATE_NOT_REG_NOT_SEARCHING, REG_STATE_HOME, REG_STATE_NOT_REG_SEARCHING,
+ REG_STATE_DENIED, REG_STATE_UNKNOWN, REG_STATE_ROAMING})
+ public @interface RegState {}
+
+ /** Not registered. The device is not currently searching a new operator to register */
+ public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
+ /** Registered on home network */
+ public static final int REG_STATE_HOME = 1;
+ /** Not registered. The device is currently searching a new operator to register */
+ public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+ /** Registration denied */
+ public static final int REG_STATE_DENIED = 3;
+ /** Registration state is unknown */
+ public static final int REG_STATE_UNKNOWN = 4;
+ /** Registered on roaming network */
+ public static final int REG_STATE_ROAMING = 5;
+
+ /**
+ * Supported service type
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SERVICE_TYPE_",
+ value = {SERVICE_TYPE_VOICE, SERVICE_TYPE_DATA, SERVICE_TYPE_SMS, SERVICE_TYPE_VIDEO,
+ SERVICE_TYPE_EMERGENCY})
+ public @interface ServiceType {}
+
+ public static final int SERVICE_TYPE_VOICE = 1;
+ public static final int SERVICE_TYPE_DATA = 2;
+ public static final int SERVICE_TYPE_SMS = 3;
+ public static final int SERVICE_TYPE_VIDEO = 4;
+ public static final int SERVICE_TYPE_EMERGENCY = 5;
+
+ /** {@link AccessNetworkConstants.TransportType}*/
+ private final int mTransportType;
+
+ @Domain
+ private final int mDomain;
+
+ @RegState
+ private final int mRegState;
+
+ private final int mAccessNetworkTechnology;
+
+ private final int mReasonForDenial;
+
+ private final boolean mEmergencyOnly;
+
+ private final int[] mAvailableServices;
+
+ @Nullable
+ private final CellIdentity mCellIdentity;
+
+
+ /**
+ * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
+ * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
+ * @param regState Network registration state.
+ * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
+ * @param reasonForDenial Reason for denial if the registration state is DENIED.
+ * @param availableServices The supported service.
+ * @param cellIdentity The identity representing a unique cell
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity) {
+ mTransportType = transportType;
+ mDomain = domain;
+ mRegState = regState;
+ mAccessNetworkTechnology = accessNetworkTechnology;
+ mReasonForDenial = reasonForDenial;
+ mAvailableServices = availableServices;
+ mCellIdentity = cellIdentity;
+ mEmergencyOnly = emergencyOnly;
+ }
+
+ protected NetworkRegistrationState(Parcel source) {
+ mTransportType = source.readInt();
+ mDomain = source.readInt();
+ mRegState = source.readInt();
+ mAccessNetworkTechnology = source.readInt();
+ mReasonForDenial = source.readInt();
+ mEmergencyOnly = source.readBoolean();
+ mAvailableServices = source.createIntArray();
+ mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+ }
+
+ /**
+ * @return The transport type.
+ */
+ public int getTransportType() { return mTransportType; }
+
+ /**
+ * @return The network domain.
+ */
+ public @Domain int getDomain() { return mDomain; }
+
+ /**
+ * @return The registration state.
+ */
+ public @RegState int getRegState() {
+ return mRegState;
+ }
+
+ /**
+ * @return Whether emergency is enabled.
+ */
+ public boolean isEmergencyEnabled() { return mEmergencyOnly; }
+
+ /**
+ * @return List of available service types.
+ */
+ public int[] getAvailableServices() { return mAvailableServices; }
+
+ /**
+ * @return The access network technology. Must be one of TelephonyManager.NETWORK_TYPE_XXXX.
+ */
+ public int getAccessNetworkTechnology() {
+ return mAccessNetworkTechnology;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private static String regStateToString(int regState) {
+ switch (regState) {
+ case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
+ case REG_STATE_HOME: return "HOME";
+ case REG_STATE_NOT_REG_SEARCHING: return "NOT_REG_SEARCHING";
+ case REG_STATE_DENIED: return "DENIED";
+ case REG_STATE_UNKNOWN: return "UNKNOWN";
+ case REG_STATE_ROAMING: return "ROAMING";
+ }
+ return "Unknown reg state " + regState;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("NetworkRegistrationState{")
+ .append("transportType=").append(mTransportType)
+ .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+ .append(" regState=").append(regStateToString(mRegState))
+ .append(" accessNetworkTechnology=")
+ .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
+ .append(" reasonForDenial=").append(mReasonForDenial)
+ .append(" emergencyEnabled=").append(mEmergencyOnly)
+ .append(" supportedServices=").append(mAvailableServices)
+ .append(" cellIdentity=").append(mCellIdentity)
+ .append("}").toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
+ mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof NetworkRegistrationState)) {
+ return false;
+ }
+
+ NetworkRegistrationState other = (NetworkRegistrationState) o;
+ return mTransportType == other.mTransportType
+ && mDomain == other.mDomain
+ && mRegState == other.mRegState
+ && mAccessNetworkTechnology == other.mAccessNetworkTechnology
+ && mReasonForDenial == other.mReasonForDenial
+ && mEmergencyOnly == other.mEmergencyOnly
+ && (mAvailableServices == other.mAvailableServices
+ || Arrays.equals(mAvailableServices, other.mAvailableServices))
+ && mCellIdentity == other.mCellIdentity;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mTransportType);
+ dest.writeInt(mDomain);
+ dest.writeInt(mRegState);
+ dest.writeInt(mAccessNetworkTechnology);
+ dest.writeInt(mReasonForDenial);
+ dest.writeBoolean(mEmergencyOnly);
+ dest.writeIntArray(mAvailableServices);
+ dest.writeParcelable(mCellIdentity, 0);
+ }
+
+ public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
+ new Parcelable.Creator<NetworkRegistrationState>() {
+ @Override
+ public NetworkRegistrationState createFromParcel(Parcel source) {
+ return new NetworkRegistrationState(source);
+ }
+
+ @Override
+ public NetworkRegistrationState[] newArray(int size) {
+ return new NetworkRegistrationState[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
new file mode 100644
index 0000000..6b3584c
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of network service. Services that extend NetworkService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_NETWORK_SERVICE". The network service definition in the manifest must
+ * follow the following format:
+ * ...
+ * <service android:name=".xxxNetworkService"
+ * android:permission="android.permission.BIND_NETWORK_SERVICE" >
+ * <intent-filter>
+ * <action android:name="android.telephony.NetworkService" />
+ * </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkService extends Service {
+
+ private final String TAG = NetworkService.class.getSimpleName();
+
+ 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 final HandlerThread mHandlerThread;
+
+ private final NetworkServiceHandler mHandler;
+
+ private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
+
+ private final SparseArray<INetworkServiceWrapper> mBinderMap = new SparseArray<>();
+
+ /**
+ * The abstract class of the actual network service implementation. The network service provider
+ * must extend this class to support network connection. Note that each instance of network
+ * service is associated with one physical SIM slot.
+ */
+ public class NetworkServiceProvider {
+ private final int mSlotId;
+
+ private final List<INetworkServiceCallback>
+ mNetworkRegistrationStateChangedCallbacks = new ArrayList<>();
+
+ public NetworkServiceProvider(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return SIM slot id the network service associated with.
+ */
+ public final int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * API to get network registration state. The result will be passed to the callback.
+ * @param domain
+ * @param callback
+ * @return SIM slot id the network service associated with.
+ */
+ public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
+ callback.onGetNetworkRegistrationStateComplete(
+ NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ public final void notifyNetworkRegistrationStateChanged() {
+ mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED,
+ mSlotId, 0, null).sendToTarget();
+ }
+
+ private void registerForStateChanged(INetworkServiceCallback callback) {
+ synchronized (mNetworkRegistrationStateChangedCallbacks) {
+ mNetworkRegistrationStateChangedCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForStateChanged(INetworkServiceCallback callback) {
+ synchronized (mNetworkRegistrationStateChangedCallbacks) {
+ mNetworkRegistrationStateChangedCallbacks.remove(callback);
+ }
+ }
+
+ private void notifyStateChangedToCallbacks() {
+ for (INetworkServiceCallback callback : mNetworkRegistrationStateChangedCallbacks) {
+ try {
+ callback.onNetworkStateChanged();
+ } catch (RemoteException exception) {
+ // Doing nothing.
+ }
+ }
+ }
+
+ /**
+ * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+ */
+ @CallSuper
+ protected void onDestroy() {
+ mNetworkRegistrationStateChangedCallbacks.clear();
+ }
+ }
+
+ private class NetworkServiceHandler extends Handler {
+
+ NetworkServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int slotId = message.arg1;
+ final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
+ NetworkServiceProvider service;
+
+ synchronized (mServiceMap) {
+ service = mServiceMap.get(slotId);
+ }
+
+ switch (message.what) {
+ case NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
+ service = createNetworkServiceProvider(message.arg1);
+ if (service != null) {
+ mServiceMap.put(slotId, service);
+ }
+ break;
+ case NETWORK_SERVICE_GET_REGISTRATION_STATE:
+ if (service == null) break;
+ int domainId = message.arg2;
+ service.getNetworkRegistrationState(domainId,
+ new NetworkServiceCallback(callback));
+
+ break;
+ case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
+ if (service == null) break;
+ service.registerForStateChanged(callback);
+ break;
+ case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
+ if (service == null) break;
+ service.unregisterForStateChanged(callback);
+ break;
+ case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
+ if (service == null) break;
+ service.notifyStateChangedToCallbacks();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /** @hide */
+ protected NetworkService() {
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ mHandler = new NetworkServiceHandler(mHandlerThread.getLooper());
+ log("network service created");
+ }
+
+ /**
+ * Create the instance of {@link NetworkServiceProvider}. Network service provider must override
+ * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
+ * will call this method after binding the network service for each active SIM slot id.
+ *
+ * @param slotId SIM slot id the network service associated with.
+ * @return Network service object
+ */
+ protected abstract NetworkServiceProvider createNetworkServiceProvider(int slotId);
+
+ /** @hide */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent == null || !NETWORK_SERVICE_INTERFACE.equals(intent.getAction())) {
+ loge("Unexpected intent " + intent);
+ 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;
+ }
+
+ /** @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);
+ }
+
+ return false;
+ }
+
+ /** @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();
+ }
+
+ /**
+ * A wrapper around INetworkService that forwards calls to implementations of
+ * {@link NetworkService}.
+ */
+ private class INetworkServiceWrapper extends INetworkService.Stub {
+
+ private final int mSlotId;
+
+ INetworkServiceWrapper(int slotId) {
+ mSlotId = slotId;
+ }
+
+ @Override
+ public void getNetworkRegistrationState(int domain, INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, mSlotId,
+ domain, callback).sendToTarget();
+ }
+
+ @Override
+ public void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, mSlotId,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, mSlotId,
+ 0, callback).sendToTarget();
+ }
+ }
+
+ private final void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private final void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
new file mode 100644
index 0000000..92ebf36
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.NetworkService.NetworkServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+/**
+ * Network service callback. Object of this class is passed to NetworkServiceProvider upon
+ * calling getNetworkRegistrationState, to receive asynchronous feedback from NetworkServiceProvider
+ * upon onGetNetworkRegistrationStateComplete. It's like a wrapper of INetworkServiceCallback
+ * because INetworkServiceCallback can't be a parameter type in public APIs.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkServiceCallback {
+
+ private static final String mTag = NetworkServiceCallback.class.getSimpleName();
+
+ /**
+ * Result of network requests
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_FAILED})
+ public @interface Result {}
+
+ /** Request is completed successfully */
+ public static final int RESULT_SUCCESS = 0;
+ /** Request is not support */
+ public static final int RESULT_ERROR_UNSUPPORTED = 1;
+ /** Request contains invalid arguments */
+ public static final int RESULT_ERROR_INVALID_ARG = 2;
+ /** Service is busy */
+ public static final int RESULT_ERROR_BUSY = 3;
+ /** Request sent in illegal state */
+ public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
+ /** Request failed */
+ public static final int RESULT_ERROR_FAILED = 5;
+
+ private final WeakReference<INetworkServiceCallback> mCallback;
+
+ /** @hide */
+ public NetworkServiceCallback(INetworkServiceCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * Called to indicate result of
+ * {@link NetworkServiceProvider#getNetworkRegistrationState(int, NetworkServiceCallback)}
+ *
+ * @param result Result status like {@link NetworkServiceCallback#RESULT_SUCCESS} or
+ * {@link NetworkServiceCallback#RESULT_ERROR_UNSUPPORTED}
+ * @param state The state information to be returned to callback.
+ */
+ public void onGetNetworkRegistrationStateComplete(int result, NetworkRegistrationState state) {
+ INetworkServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onGetNetworkRegistrationStateComplete(result, state);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 98ea451..0ee870a 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -251,7 +251,15 @@
*/
public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
- /*
+ /**
+ * Listen for changes to the physical channel configuration.
+ *
+ * @see #onPhysicalChannelConfigurationChanged
+ * @hide
+ */
+ public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
+
+ /*
* Subscription used to listen to the phone state changes
* @hide
*/
@@ -362,7 +370,10 @@
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
-
+ case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
+ PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
+ (List<PhysicalChannelConfig>)msg.obj);
+ break;
}
}
};
@@ -561,6 +572,16 @@
}
/**
+ * Callback invoked when the current physical channel configuration has changed
+ *
+ * @param configs List of the current {@link PhysicalChannelConfig}s
+ * @hide
+ */
+ public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.aidl b/telephony/java/android/telephony/PhysicalChannelConfig.aidl
new file mode 100644
index 0000000..651c103
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable PhysicalChannelConfig;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
new file mode 100644
index 0000000..651d68d
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class PhysicalChannelConfig implements Parcelable {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+ public @interface ConnectionStatus {}
+
+ /**
+ * UE has connection to cell for signalling and possibly data (3GPP 36.331, 25.331).
+ */
+ public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+ /**
+ * UE has connection to cell for data (3GPP 36.331, 25.331).
+ */
+ public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+ /**
+ * Connection status of the cell.
+ *
+ * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
+ */
+ private int mCellConnectionStatus;
+
+ /**
+ * Cell bandwidth, in kHz.
+ */
+ private int mCellBandwidthDownlinkKhz;
+
+ public PhysicalChannelConfig(int status, int bandwidth) {
+ mCellConnectionStatus = status;
+ mCellBandwidthDownlinkKhz = bandwidth;
+ }
+
+ public PhysicalChannelConfig(Parcel in) {
+ mCellConnectionStatus = in.readInt();
+ mCellBandwidthDownlinkKhz = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCellConnectionStatus);
+ dest.writeInt(mCellBandwidthDownlinkKhz);
+ }
+
+ /**
+ * @return Cell bandwidth, in kHz
+ */
+ public int getCellBandwidthDownlink() {
+ return mCellBandwidthDownlinkKhz;
+ }
+
+ /**
+ * Gets the connection status of the cell.
+ *
+ * @see #CONNECTION_PRIMARY_SERVING
+ * @see #CONNECTION_SECONDARY_SERVING
+ *
+ * @return Connection status of the cell
+ */
+ @ConnectionStatus
+ public int getConnectionStatus() {
+ return mCellConnectionStatus;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof PhysicalChannelConfig)) {
+ return false;
+ }
+
+ PhysicalChannelConfig config = (PhysicalChannelConfig) o;
+ return mCellConnectionStatus == config.mCellConnectionStatus
+ && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+ }
+
+ public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
+ new Parcelable.Creator<PhysicalChannelConfig>() {
+ public PhysicalChannelConfig createFromParcel(Parcel in) {
+ return new PhysicalChannelConfig(in);
+ }
+
+ public PhysicalChannelConfig[] newArray(int size) {
+ return new PhysicalChannelConfig[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d4b4b88..77706e8 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.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,6 +26,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Contains phone state and service related information.
*
@@ -286,6 +290,8 @@
* Reference: 3GPP TS 36.104 5.4.3 */
private int mLteEarfcnRsrpBoost = 0;
+ private List<NetworkRegistrationState> mNetworkRegistrationStates = new ArrayList<>();
+
/**
* get String description of roaming type
* @hide
@@ -366,6 +372,7 @@
mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
+ mNetworkRegistrationStates = new ArrayList<>(s.mNetworkRegistrationStates);
}
/**
@@ -396,6 +403,8 @@
mIsDataRoamingFromRegistration = in.readInt() != 0;
mIsUsingCarrierAggregation = in.readInt() != 0;
mLteEarfcnRsrpBoost = in.readInt();
+ mNetworkRegistrationStates = new ArrayList<>();
+ in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
}
public void writeToParcel(Parcel out, int flags) {
@@ -423,6 +432,7 @@
out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
out.writeInt(mLteEarfcnRsrpBoost);
+ out.writeList(mNetworkRegistrationStates);
}
public int describeContents() {
@@ -751,13 +761,14 @@
s.mCdmaDefaultRoamingIndicator)
&& mIsEmergencyOnly == s.mIsEmergencyOnly
&& mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
- && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
+ && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation)
+ && mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates);
}
/**
* Convert radio technology to String
*
- * @param radioTechnology
+ * @param rt radioTechnology
* @return String representation of the RAT
*
* @hide
@@ -884,6 +895,7 @@
.append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
.append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
.append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+ .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates)
.append("}").toString();
}
@@ -913,6 +925,7 @@
mIsDataRoamingFromRegistration = false;
mIsUsingCarrierAggregation = false;
mLteEarfcnRsrpBoost = 0;
+ mNetworkRegistrationStates = new ArrayList<>();
}
public void setStateOutOfService() {
@@ -1394,4 +1407,52 @@
return newSs;
}
+
+ /**
+ * Get all of the available network registration states.
+ *
+ * @return List of registration states
+ * @hide
+ */
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates() {
+ return mNetworkRegistrationStates;
+ }
+
+ /**
+ * Get the network registration states with given transport type.
+ *
+ * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+ * @return List of registration states.
+ * @hide
+ */
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ List<NetworkRegistrationState> list = new ArrayList<>();
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getTransportType() == transportType) {
+ list.add(networkRegistrationState);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Get the network registration states with given transport type and domain.
+ *
+ * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+ * @param domain The network domain. Must be DOMAIN_CS or DOMAIN_PS.
+ * @return The matching NetworkRegistrationState.
+ * @hide
+ */
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getTransportType() == transportType
+ && networkRegistrationState.getDomain() == domain) {
+ return networkRegistrationState;
+ }
+ }
+ return null;
+ }
}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 4f6f68c..83d9bd9 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -384,6 +384,13 @@
/** Call/IMS registration is failed/dropped because of a network detach */
public static final int CODE_NETWORK_DETACH = 1513;
+ /**
+ * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+ * emergency number". This scenario is important in some regions where the carrier network will
+ * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+ */
+ public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
/* OEM specific error codes. To be used by OEMs when they don't want to
reveal error code which would be replaced by ERROR_UNSPECIFIED */
public static final int CODE_OEM_CAUSE_1 = 0xf001;
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 801a396..66e0955 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -229,7 +228,7 @@
anyInt(),
anyString(),
anyString(),
- anyLong(),
+ anyInt(),
eq(TEST_SPI),
anyInt(),
anyInt(),
@@ -264,7 +263,7 @@
anyInt(),
anyString(),
anyString(),
- anyLong(),
+ anyInt(),
eq(TEST_SPI),
anyInt(),
anyInt(),