Merge "Permit privileged system apps to send SMS without persisting."
diff --git a/api/current.txt b/api/current.txt
index a53a4bc..f3cb3fc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8188,6 +8188,7 @@
     field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
+    field public static final java.lang.String IPSEC_SERVICE = "ipsec";
     field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
     field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
     field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8553,6 +8554,7 @@
     field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+    field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -23723,6 +23725,68 @@
     field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
   }
 
+  public final class IpSecAlgorithm implements android.os.Parcelable {
+    ctor public IpSecAlgorithm(java.lang.String, byte[]);
+    ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+    method public int describeContents();
+    method public byte[] getKey();
+    method public java.lang.String getName();
+    method public int getTruncationLengthBits();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+    field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+    field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+  }
+
+  public final class IpSecManager {
+    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+  }
+
+  public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+  }
+
+  public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getPort();
+    method public java.io.FileDescriptor getSocket();
+  }
+
+  public final class IpSecTransform implements java.lang.AutoCloseable {
+    method public void close();
+    field public static final int DIRECTION_IN = 0; // 0x0
+    field public static final int DIRECTION_OUT = 1; // 0x1
+  }
+
+  public static class IpSecTransform.Builder {
+    ctor public IpSecTransform.Builder(android.content.Context);
+    method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+    method public android.net.IpSecTransform.Builder setSpi(int, int);
+    method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+  }
+
   public class LinkAddress implements android.os.Parcelable {
     method public int describeContents();
     method public java.net.InetAddress getAddress();
@@ -24915,8 +24979,6 @@
   public class DiscoverySession {
     method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
     method public void destroy();
-    method public static int getMaxSendRetryCount();
-    method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
 
@@ -36695,9 +36757,6 @@
     field public static final int RTT_MODE_VCO = 3; // 0x3
   }
 
-  public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation {
-  }
-
   public final class CallAudioState implements android.os.Parcelable {
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 46bb48a..f65506a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8522,6 +8522,7 @@
     field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
+    field public static final java.lang.String IPSEC_SERVICE = "ipsec";
     field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
     field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
     field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8895,6 +8896,7 @@
     field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+    field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -25560,6 +25562,73 @@
     field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
   }
 
+  public final class IpSecAlgorithm implements android.os.Parcelable {
+    ctor public IpSecAlgorithm(java.lang.String, byte[]);
+    ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+    method public int describeContents();
+    method public byte[] getKey();
+    method public java.lang.String getName();
+    method public int getTruncationLengthBits();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+    field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+    field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+  }
+
+  public final class IpSecManager {
+    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+    method public void removeTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+  }
+
+  public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+  }
+
+  public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getPort();
+    method public java.io.FileDescriptor getSocket();
+  }
+
+  public final class IpSecTransform implements java.lang.AutoCloseable {
+    method public void close();
+    field public static final int DIRECTION_IN = 0; // 0x0
+    field public static final int DIRECTION_OUT = 1; // 0x1
+  }
+
+  public static class IpSecTransform.Builder {
+    ctor public IpSecTransform.Builder(android.content.Context);
+    method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, java.net.InetAddress);
+    method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+    method public android.net.IpSecTransform.Builder setNattKeepalive(int);
+    method public android.net.IpSecTransform.Builder setSpi(int, int);
+    method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+    method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
+  }
+
   public class LinkAddress implements android.os.Parcelable {
     method public int describeContents();
     method public java.net.InetAddress getAddress();
@@ -27468,8 +27537,6 @@
   public class DiscoverySession {
     method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
     method public void destroy();
-    method public static int getMaxSendRetryCount();
-    method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
 
@@ -39668,9 +39735,6 @@
     field public static final int RTT_MODE_VCO = 3; // 0x3
   }
 
-  public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation {
-  }
-
   public final class CallAudioState implements android.os.Parcelable {
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index e93f5ed..c7f7fd1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8199,6 +8199,7 @@
     field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
+    field public static final java.lang.String IPSEC_SERVICE = "ipsec";
     field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
     field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
     field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8565,6 +8566,7 @@
     field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+    field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -23796,6 +23798,68 @@
     field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
   }
 
+  public final class IpSecAlgorithm implements android.os.Parcelable {
+    ctor public IpSecAlgorithm(java.lang.String, byte[]);
+    ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+    method public int describeContents();
+    method public byte[] getKey();
+    method public java.lang.String getName();
+    method public int getTruncationLengthBits();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+    field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+    field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+    field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+  }
+
+  public final class IpSecManager {
+    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+  }
+
+  public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+  }
+
+  public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+    method public int getSpi();
+  }
+
+  public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+    method public void close();
+    method public int getPort();
+    method public java.io.FileDescriptor getSocket();
+  }
+
+  public final class IpSecTransform implements java.lang.AutoCloseable {
+    method public void close();
+    field public static final int DIRECTION_IN = 0; // 0x0
+    field public static final int DIRECTION_OUT = 1; // 0x1
+  }
+
+  public static class IpSecTransform.Builder {
+    ctor public IpSecTransform.Builder(android.content.Context);
+    method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+    method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+    method public android.net.IpSecTransform.Builder setSpi(int, int);
+    method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+  }
+
   public class LinkAddress implements android.os.Parcelable {
     method public int describeContents();
     method public java.net.InetAddress getAddress();
@@ -24988,8 +25052,6 @@
   public class DiscoverySession {
     method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
     method public void destroy();
-    method public static int getMaxSendRetryCount();
-    method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
 
@@ -36777,9 +36839,6 @@
     field public static final int RTT_MODE_VCO = 3; // 0x3
   }
 
-  public static abstract class Call.RttCall.RttAudioMode implements java.lang.annotation.Annotation {
-  }
-
   public final class CallAudioState implements android.os.Parcelable {
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 02b86fe..46d3835 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2662,6 +2662,7 @@
             VIBRATOR_SERVICE,
             //@hide: STATUS_BAR_SERVICE,
             CONNECTIVITY_SERVICE,
+            IPSEC_SERVICE,
             //@hide: UPDATE_LOCK_SERVICE,
             //@hide: NETWORKMANAGEMENT_SERVICE,
             NETWORK_STATS_SERVICE,
@@ -2763,6 +2764,9 @@
      *  <dt> {@link #CONNECTIVITY_SERVICE} ("connection")
      *  <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
      *  handling management of network connections.
+     *  <dt> {@link #IPSEC_SERVICE} ("ipsec")
+     *  <dd> A {@link android.net.IpSecManager IpSecManager} for managing IPSec on
+     *  sockets and networks.
      *  <dt> {@link #WIFI_SERVICE} ("wifi")
      *  <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi
      *  connectivity.  On releases before NYC, it should only be obtained from an application
@@ -3093,6 +3097,15 @@
     public static final String CONNECTIVITY_SERVICE = "connectivity";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
+     * IPSec.
+     *
+     * @see #getSystemService
+     */
+    public static final String IPSEC_SERVICE = "ipsec";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.os.IUpdateLock} for managing runtime sequences that
      * must not be interrupted by headless OTA application or similar.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c6aaa48..b7876d9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1096,6 +1096,15 @@
     public static final String ACTION_SIM_ACTIVATION_REQUEST =
             "android.intent.action.SIM_ACTIVATION_REQUEST";
     /**
+     * Activity Action: Main entry point for carrier setup apps.
+     * <p>Carrier apps that provide an implementation for this action may be invoked to configure
+     * carrier service and typically require
+     * {@link android.telephony.TelephonyManager#hasCarrierPrivileges() carrier privileges} to
+     * fulfill their duties.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
+    /**
      * Activity Action: Send a message to someone specified by the data.
      * <p>Input: {@link #getData} is URI describing the target.
      * <p>Output: nothing.
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
new file mode 100644
index 0000000..da5cb37
--- /dev/null
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
+ * RFC 4301.
+ */
+public final class IpSecAlgorithm implements Parcelable {
+
+    /**
+     * AES-CBC Encryption/Ciphering Algorithm.
+     *
+     * <p>Valid lengths for this key are {128, 192, 256}.
+     */
+    public static final String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+
+    /**
+     * MD5 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in new
+     * applications and is provided for legacy compatibility with 3gpp infrastructure.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+     */
+    public static final String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+
+    /**
+     * SHA1 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in
+     * new applications and is provided for legacy compatibility with 3gpp infrastructure.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+     */
+    public static final String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+
+    /**
+     * SHA256 HMAC Authentication/Integrity Algorithm.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+     */
+    public static final String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+
+    /**
+     * SHA384 HMAC Authentication/Integrity Algorithm.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+     */
+    public static final String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+    /**
+     * SHA512 HMAC Authentication/Integrity Algorithm
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+     */
+    public static final String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+
+    /** @hide */
+    @StringDef({
+        ALGO_CRYPT_AES_CBC,
+        ALGO_AUTH_HMAC_MD5,
+        ALGO_AUTH_HMAC_SHA1,
+        ALGO_AUTH_HMAC_SHA256,
+        ALGO_AUTH_HMAC_SHA512
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AlgorithmName {}
+
+    private final String mName;
+    private final byte[] mKey;
+    private final int mTruncLenBits;
+
+    /**
+     * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
+     * algorithm
+     *
+     * @param algorithm type for IpSec.
+     * @param key non-null Key padded to a multiple of 8 bits.
+     */
+    public IpSecAlgorithm(String algorithm, byte[] key) {
+        this(algorithm, key, key.length * 8);
+    }
+
+    /**
+     * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
+     * algorithm
+     *
+     * @param algoName precise name of the algorithm to be used.
+     * @param key non-null Key padded to a multiple of 8 bits.
+     * @param truncLenBits the number of bits of output hash to use; only meaningful for
+     *     Authentication.
+     */
+    public IpSecAlgorithm(@AlgorithmName String algoName, byte[] key, int truncLenBits) {
+        if (!isTruncationLengthValid(algoName, truncLenBits)) {
+            throw new IllegalArgumentException("Unknown algorithm or invalid length");
+        }
+        mName = algoName;
+        mKey = key.clone();
+        mTruncLenBits = Math.min(truncLenBits, key.length * 8);
+    }
+
+    /** Retrieve the algorithm name */
+    public String getName() {
+        return mName;
+    }
+
+    /** Retrieve the key for this algorithm */
+    public byte[] getKey() {
+        return mKey.clone();
+    }
+
+    /**
+     * Retrieve the truncation length, in bits, for the key in this algo. By default this will be
+     * the length in bits of the key.
+     */
+    public int getTruncationLengthBits() {
+        return mTruncLenBits;
+    }
+
+    /* Parcelable Implementation */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Write to parcel */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mName);
+        out.writeByteArray(mKey);
+        out.writeInt(mTruncLenBits);
+    }
+
+    /** Parcelable Creator */
+    public static final Parcelable.Creator<IpSecAlgorithm> CREATOR =
+            new Parcelable.Creator<IpSecAlgorithm>() {
+                public IpSecAlgorithm createFromParcel(Parcel in) {
+                    return new IpSecAlgorithm(in);
+                }
+
+                public IpSecAlgorithm[] newArray(int size) {
+                    return new IpSecAlgorithm[size];
+                }
+            };
+
+    private IpSecAlgorithm(Parcel in) {
+        mName = in.readString();
+        mKey = in.createByteArray();
+        mTruncLenBits = in.readInt();
+    }
+
+    private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
+        switch (algo) {
+            case ALGO_AUTH_HMAC_MD5:
+                return (truncLenBits >= 96 && truncLenBits <= 128);
+            case ALGO_AUTH_HMAC_SHA1:
+                return (truncLenBits >= 96 && truncLenBits <= 160);
+            case ALGO_AUTH_HMAC_SHA256:
+                return (truncLenBits >= 96 && truncLenBits <= 256);
+            case ALGO_AUTH_HMAC_SHA384:
+                return (truncLenBits >= 192 && truncLenBits <= 384);
+            case ALGO_AUTH_HMAC_SHA512:
+                return (truncLenBits >= 256 && truncLenBits <= 512);
+            default:
+                return false;
+        }
+    }
+};
diff --git a/core/java/android/net/IpSecConfig.aidl b/core/java/android/net/IpSecConfig.aidl
new file mode 100644
index 0000000..eaefca7
--- /dev/null
+++ b/core/java/android/net/IpSecConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/** @hide */
+parcelable IpSecConfig;
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
new file mode 100644
index 0000000..b58bf42
--- /dev/null
+++ b/core/java/android/net/IpSecConfig.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/** @hide */
+public final class IpSecConfig implements Parcelable {
+    private static final String TAG = IpSecConfig.class.getSimpleName();
+
+    //MODE_TRANSPORT or MODE_TUNNEL
+    int mode;
+
+    // For tunnel mode
+    InetAddress localAddress;
+
+    InetAddress remoteAddress;
+
+    // Limit selection by network interface
+    Network network;
+
+    public static class Flow {
+        // Minimum requirements for identifying a transform
+        // SPI identifying the IPsec flow in packet processing
+        // and a remote IP address
+        int spi;
+
+        // Encryption Algorithm
+        IpSecAlgorithm encryptionAlgo;
+
+        // Authentication Algorithm
+        IpSecAlgorithm authenticationAlgo;
+    }
+
+    Flow[] flow = new Flow[2];
+
+    // For tunnel mode IPv4 UDP Encapsulation
+    // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
+    int encapType;
+    int encapLocalPort;
+    int encapRemotePort;
+
+    // An optional protocol to match with the selector
+    int selectorProto;
+
+    // A bitmask of FEATURE_* indicating which of the fields
+    // of this class are valid.
+    long features;
+
+    // An interval, in seconds between the NattKeepalive packets
+    int nattKeepaliveInterval;
+
+    public InetAddress getLocalIp() {
+        return localAddress;
+    }
+
+    public int getSpi(int direction) {
+        return flow[direction].spi;
+    }
+
+    public InetAddress getRemoteIp() {
+        return remoteAddress;
+    }
+
+    public IpSecAlgorithm getEncryptionAlgo(int direction) {
+        return flow[direction].encryptionAlgo;
+    }
+
+    public IpSecAlgorithm getAuthenticationAlgo(int direction) {
+        return flow[direction].authenticationAlgo;
+    }
+
+    Network getNetwork() {
+        return network;
+    }
+
+    public int getEncapType() {
+        return encapType;
+    }
+
+    public int getEncapLocalPort() {
+        return encapLocalPort;
+    }
+
+    public int getEncapRemotePort() {
+        return encapRemotePort;
+    }
+
+    public int getSelectorProto() {
+        return selectorProto;
+    }
+
+    int getNattKeepaliveInterval() {
+        return nattKeepaliveInterval;
+    }
+
+    public boolean hasProperty(int featureBits) {
+        return (features & featureBits) == featureBits;
+    }
+
+    // Parcelable Methods
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(features);
+        // TODO: Use a byte array or other better method for storing IPs that can also include scope
+        out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
+        // TODO: Use a byte array or other better method for storing IPs that can also include scope
+        out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
+        out.writeParcelable(network, flags);
+        out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags);
+        out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags);
+        out.writeInt(encapType);
+        out.writeInt(encapLocalPort);
+        out.writeInt(encapRemotePort);
+        out.writeInt(selectorProto);
+    }
+
+    // Package Private: Used by the IpSecTransform.Builder;
+    // there should be no public constructor for this object
+    IpSecConfig() {
+        flow[IpSecTransform.DIRECTION_IN].spi = 0;
+        flow[IpSecTransform.DIRECTION_OUT].spi = 0;
+        nattKeepaliveInterval = 0; //FIXME constant
+    }
+
+    private static InetAddress readInetAddressFromParcel(Parcel in) {
+        String addrString = in.readString();
+        if (addrString == null) {
+            return null;
+        }
+        try {
+            return InetAddress.getByName(addrString);
+        } catch (UnknownHostException e) {
+            Log.wtf(TAG, "Invalid IpAddress " + addrString);
+            return null;
+        }
+    }
+
+    private IpSecConfig(Parcel in) {
+        features = in.readLong();
+        localAddress = readInetAddressFromParcel(in);
+        remoteAddress = readInetAddressFromParcel(in);
+        network = (Network) in.readParcelable(Network.class.getClassLoader());
+        flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
+        flow[IpSecTransform.DIRECTION_IN].encryptionAlgo =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        flow[IpSecTransform.DIRECTION_IN].authenticationAlgo =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
+        flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo =
+                (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+        encapType = in.readInt();
+        encapLocalPort = in.readInt();
+        encapRemotePort = in.readInt();
+        selectorProto = in.readInt();
+    }
+
+    public static final Parcelable.Creator<IpSecConfig> CREATOR =
+            new Parcelable.Creator<IpSecConfig>() {
+                public IpSecConfig createFromParcel(Parcel in) {
+                    return new IpSecConfig(in);
+                }
+
+                public IpSecConfig[] newArray(int size) {
+                    return new IpSecConfig[size];
+                }
+            };
+}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
new file mode 100644
index 0000000..2c544e9
--- /dev/null
+++ b/core/java/android/net/IpSecManager.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
+import android.util.AndroidException;
+import dalvik.system.CloseGuard;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * This class contains methods for managing IPsec sessions, which will perform kernel-space
+ * encryption and decryption of socket or Network traffic.
+ *
+ * <p>An IpSecManager may be obtained by calling {@link
+ * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link
+ * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE}
+ */
+public final class IpSecManager {
+    private static final String TAG = "IpSecManager";
+
+    /**
+     * Indicates that the combination of remote InetAddress and SPI was non-unique for a given
+     * request. If encountered, selection of a new SPI is required before a transform may be
+     * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random
+     * or reserved using reserveSecurityParameterIndex.
+     */
+    public static final class SpiUnavailableException extends AndroidException {
+        private final int mSpi;
+
+        /**
+         * Construct an exception indicating that a transform with the given SPI is already in use
+         * or otherwise unavailable.
+         *
+         * @param msg Description indicating the colliding SPI
+         * @param spi the SPI that could not be used due to a collision
+         */
+        SpiUnavailableException(String msg, int spi) {
+            super(msg + "(spi: " + spi + ")");
+            mSpi = spi;
+        }
+
+        /** Retrieve the SPI that caused a collision */
+        public int getSpi() {
+            return mSpi;
+        }
+    }
+
+    /**
+     * Indicates that the requested system resource for IPsec, such as a socket or other system
+     * resource is unavailable. If this exception is thrown, try releasing allocated objects of the
+     * type requested.
+     */
+    public static final class ResourceUnavailableException extends AndroidException {
+
+        ResourceUnavailableException(String msg) {
+            super(msg);
+        }
+    }
+
+    private final Context mContext;
+    private final INetworkManagementService mService;
+
+    public static final class SecurityParameterIndex implements AutoCloseable {
+        private final Context mContext;
+        private final InetAddress mDestinationAddress;
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+        private int mSpi;
+
+        /** Return the underlying SPI held by this object */
+        public int getSpi() {
+            return mSpi;
+        }
+
+        private SecurityParameterIndex(Context context, InetAddress destinationAddress, int spi)
+                throws ResourceUnavailableException, SpiUnavailableException {
+            mContext = context;
+            mDestinationAddress = destinationAddress;
+            mSpi = spi;
+            mCloseGuard.open("open");
+        }
+
+        /**
+         * Release an SPI that was previously reserved.
+         *
+         * <p>Release an SPI for use by other users in the system. This will fail if the SPI is
+         * currently in use by an IpSecTransform.
+         *
+         * @param destinationAddress SPIs must be unique for each combination of SPI and destination
+         *     address. Thus, the destinationAddress to which the SPI will communicate must be
+         *     supplied.
+         * @param spi the previously reserved SPI to be freed.
+         */
+        @Override
+        public void close() {
+            mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
+            mCloseGuard.close();
+        }
+
+        @Override
+        protected void finalize() {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+
+            close();
+        }
+    }
+
+    /**
+     * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
+     *
+     * <p>No IPsec packet may contain an SPI of 0.
+     */
+    public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
+
+    /**
+     * Reserve an SPI for traffic bound towards the specified destination address.
+     *
+     * <p>If successful, this SPI is guaranteed available until released by a call to {@link
+     * SecurityParameterIndex#close()}.
+     *
+     * @param destinationAddress SPIs must be unique for each combination of SPI and destination
+     *     address.
+     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI.
+     * @return the reserved SecurityParameterIndex
+     * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
+     *     for this user
+     * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+     */
+    public SecurityParameterIndex reserveSecurityParameterIndex(
+            InetAddress destinationAddress, int requestedSpi)
+            throws SpiUnavailableException, ResourceUnavailableException {
+        return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi);
+    }
+
+    /**
+     * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec
+     * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
+     * transform. For security reasons, attempts to send traffic to any IP address other than the
+     * address associated with that transform will throw an IOException. In addition, if the
+     * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
+     * send() or receive() until the transform is removed from the socket by calling {@link
+     * #removeTransportModeTransform(Socket, IpSecTransform)};
+     *
+     * @param socket a stream socket
+     * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+     */
+    public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+            throws IOException {
+        applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
+    }
+
+    /**
+     * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec
+     * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
+     * transform. For security reasons, attempts to send traffic to any IP address other than the
+     * address associated with that transform will throw an IOException. In addition, if the
+     * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
+     * send() or receive() until the transform is removed from the socket by calling {@link
+     * #removeTransportModeTransform(DatagramSocket, IpSecTransform)};
+     *
+     * @param socket a datagram socket
+     * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+     */
+    public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+            throws IOException {
+        applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
+    }
+
+    /* Call down to activate a transform */
+    private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+
+    /**
+     * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
+     * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to
+     * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic).
+     * Applications should probably not use this API directly. Instead, they should use {@link
+     * VpnService} to provide VPN capability in a more generic fashion.
+     *
+     * @param net a {@link Network} that will be tunneled via IP Sec.
+     * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
+     * @hide
+     */
+    @SystemApi
+    public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
+
+    /**
+     * Remove a transform from a given stream socket. Once removed, traffic on the socket will not
+     * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
+     * communication in the clear in the event socket reuse is desired. This operation will succeed
+     * regardless of the underlying state of a transform. If a transform is removed, communication
+     * on all sockets to which that transform was applied will fail until this method is called.
+     *
+     * @param socket a socket that previously had a transform applied to it.
+     * @param transform the IPsec Transform that was previously applied to the given socket
+     */
+    public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
+        removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
+    }
+
+    /**
+     * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not
+     * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
+     * communication in the clear in the event socket reuse is desired. This operation will succeed
+     * regardless of the underlying state of a transform. If a transform is removed, communication
+     * on all sockets to which that transform was applied will fail until this method is called.
+     *
+     * @param socket a socket that previously had a transform applied to it.
+     * @param transform the IPsec Transform that was previously applied to the given socket
+     */
+    public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
+        removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
+    }
+
+    /* Call down to activate a transform */
+    private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+
+    /**
+     * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
+     * cleanup if a tunneled Network experiences a change in default route. The Network will drop
+     * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
+     * lost, all traffic will drop.
+     *
+     * @param net a network that currently has transform applied to it.
+     * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
+     *     network
+     * @hide
+     */
+    @SystemApi
+    public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
+
+    /**
+     * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for
+     * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic.
+     *
+     * <p>The socket provided by this class cannot be re-bound or closed via the inner
+     * FileDescriptor. Instead, disposing of this socket requires a call to close().
+     */
+    public static final class UdpEncapsulationSocket implements AutoCloseable {
+        private final FileDescriptor mFd;
+        private final Context mContext;
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+
+        private UdpEncapsulationSocket(Context context, int port)
+                throws ResourceUnavailableException {
+            mContext = context;
+            mCloseGuard.open("constructor");
+            // TODO: go down to the kernel and get a socket on the specified
+            mFd = new FileDescriptor();
+        }
+
+        private UdpEncapsulationSocket(Context context) throws ResourceUnavailableException {
+            mContext = context;
+            mCloseGuard.open("constructor");
+            // TODO: go get a random socket on a random port
+            mFd = new FileDescriptor();
+        }
+
+        /** Access the inner UDP Encapsulation Socket */
+        public FileDescriptor getSocket() {
+            return mFd;
+        }
+
+        /** Retrieve the port number of the inner encapsulation socket */
+        public int getPort() {
+            return 0; // TODO get the port number from the Socket;
+        }
+
+        @Override
+        /**
+         * Release the resources that have been reserved for this Socket.
+         *
+         * <p>This method closes the underlying socket, reducing a user's allocated sockets in the
+         * system. This must be done as part of cleanup following use of a socket. Failure to do so
+         * will cause the socket to count against a total allocation limit for IpSec and eventually
+         * fail due to resource limits.
+         *
+         * @param fd a file descriptor previously returned as a UDP Encapsulation socket.
+         */
+        public void close() {
+            // TODO: Go close the socket
+            mCloseGuard.close();
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+
+            close();
+        }
+    };
+
+    /**
+     * Open a socket that is bound to a free UDP port on the system.
+     *
+     * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
+     * the caller. This provides safe access to a socket on a port that can later be used as a UDP
+     * Encapsulation port.
+     *
+     * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
+     * socket port. Explicitly opening this port is only necessary if communication is desired on
+     * that port.
+     *
+     * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this
+     *     method will bind to the specified port or fail. To retrieve the port number, call {@link
+     *     android.system.Os#getsockname(FileDescriptor)}.
+     * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime
+     *     of the object.
+     */
+    // Returning a socket in this fashion that has been created and bound by the system
+    // is the only safe way to ensure that a socket is both accessible to the user and
+    // safely usable for Encapsulation without allowing a user to possibly unbind from/close
+    // the port, which could potentially impact the traffic of the next user who binds to that
+    // socket.
+    public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
+            throws IOException, ResourceUnavailableException {
+        // Temporary code
+        return new UdpEncapsulationSocket(mContext, port);
+    }
+
+    /**
+     * Open a socket that is bound to a port selected by the system.
+     *
+     * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
+     * the caller. This provides safe access to a socket on a port that can later be used as a UDP
+     * Encapsulation port.
+     *
+     * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
+     * socket port. Explicitly opening this port is only necessary if communication is desired on
+     * that port.
+     *
+     * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port
+     */
+    // Returning a socket in this fashion that has been created and bound by the system
+    // is the only safe way to ensure that a socket is both accessible to the user and
+    // safely usable for Encapsulation without allowing a user to possibly unbind from/close
+    // the port, which could potentially impact the traffic of the next user who binds to that
+    // socket.
+    public UdpEncapsulationSocket openUdpEncapsulationSocket()
+            throws IOException, ResourceUnavailableException {
+        // Temporary code
+        return new UdpEncapsulationSocket(mContext);
+    }
+
+    /**
+     * Retrieve an instance of an IpSecManager within you application context
+     *
+     * @param context the application context for this manager
+     * @hide
+     */
+    public IpSecManager(Context context, INetworkManagementService service) {
+        mContext = checkNotNull(context, "missing context");
+        mService = checkNotNull(service, "missing service");
+    }
+}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
new file mode 100644
index 0000000..d6dd28b
--- /dev/null
+++ b/core/java/android/net/IpSecTransform.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.system.ErrnoException;
+import android.util.Log;
+import dalvik.system.CloseGuard;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+
+/**
+ * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec.
+ *
+ * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout
+ * the lifetime of the underlying transform. If a transform object leaves scope, the underlying
+ * transform may be disabled automatically, with likely undesirable results.
+ *
+ * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
+ * of traffic or may represent a transport mode transform operating on a Socket or Sockets.
+ */
+public final class IpSecTransform implements AutoCloseable {
+    private static final String TAG = "IpSecTransform";
+
+    /**
+     * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
+     * to traffic towards the host.
+     */
+    public static final int DIRECTION_IN = 0;
+
+    /**
+     * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
+     * to traffic from the host.
+     */
+    public static final int DIRECTION_OUT = 1;
+
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransformDirection {}
+
+    /** @hide */
+    private static final int MODE_TUNNEL = 0;
+
+    /** @hide */
+    private static final int MODE_TRANSPORT = 1;
+
+    /** @hide */
+    public static final int ENCAP_NONE = 0;
+
+    /**
+     * IpSec traffic will be encapsulated within UDP as per <a
+     * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>.
+     *
+     * @hide
+     */
+    public static final int ENCAP_ESPINUDP = 1;
+
+    /**
+     * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad
+     * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP.
+     *
+     * @hide
+     */
+    public static final int ENCAP_ESPINUDP_NONIKE = 2;
+
+    /** @hide */
+    @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NONIKE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EncapType {}
+
+    /**
+     * Sentinel for an invalid transform (means that this transform is inactive).
+     *
+     * @hide
+     */
+    public static final int INVALID_TRANSFORM_ID = -1;
+
+    private IpSecTransform(Context context, IpSecConfig config) {
+        mContext = context;
+        mConfig = config;
+        mTransformId = INVALID_TRANSFORM_ID;
+    }
+
+    private IpSecTransform activate()
+            throws IOException, IpSecManager.ResourceUnavailableException,
+                    IpSecManager.SpiUnavailableException {
+        int transformId;
+        synchronized (this) {
+            //try {
+            transformId = INVALID_TRANSFORM_ID;
+            //} catch (RemoteException e) {
+            //    throw e.rethrowFromSystemServer();
+            //}
+
+            if (transformId < 0) {
+                throw new ErrnoException("addTransform", -transformId).rethrowAsIOException();
+            }
+
+            startKeepalive(mContext); // Will silently fail if not required
+            mTransformId = transformId;
+            Log.d(TAG, "Added Transform with Id " + transformId);
+        }
+        mCloseGuard.open("build");
+
+        return this;
+    }
+
+    /**
+     * Deactivate an IpSecTransform and free all resources for that transform that are managed by
+     * the system for this Transform.
+     *
+     * <p>Deactivating a transform while it is still applied to any Socket will result in sockets
+     * refusing to send or receive data. This method will silently succeed if the specified
+     * transform has already been removed; thus, it is always safe to attempt cleanup when a
+     * transform is no longer needed.
+     */
+    public void close() {
+        Log.d(TAG, "Removing Transform with Id " + mTransformId);
+
+        // Always safe to attempt cleanup
+        if (mTransformId == INVALID_TRANSFORM_ID) {
+            return;
+        }
+        //try {
+        stopKeepalive();
+        //} catch (RemoteException e) {
+        //    transform.setTransformId(transformId);
+        //    throw e.rethrowFromSystemServer();
+        //} finally {
+        mTransformId = INVALID_TRANSFORM_ID;
+        //}
+        mCloseGuard.close();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
+        }
+        close();
+    }
+
+    /* Package */
+    IpSecConfig getConfig() {
+        return mConfig;
+    }
+
+    private final IpSecConfig mConfig;
+    private int mTransformId;
+    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 =
+            new ConnectivityManager.PacketKeepaliveCallback() {
+
+                @Override
+                public void onStarted() {
+                    synchronized (mKeepaliveSyncLock) {
+                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
+                        mKeepaliveSyncLock.notifyAll();
+                    }
+                }
+
+                @Override
+                public void onStopped() {
+                    synchronized (mKeepaliveSyncLock) {
+                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
+                        mKeepaliveSyncLock.notifyAll();
+                    }
+                }
+
+                @Override
+                public void onError(int error) {
+                    synchronized (mKeepaliveSyncLock) {
+                        mKeepaliveStatus = error;
+                        mKeepaliveSyncLock.notifyAll();
+                    }
+                }
+            };
+
+    /* Package */
+    void startKeepalive(Context c) {
+        if (mConfig.getNattKeepaliveInterval() == 0) {
+            return;
+        }
+
+        ConnectivityManager cm =
+                (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        if (mKeepalive != null) {
+            Log.e(TAG, "Keepalive already started for this IpSecTransform.");
+            return;
+        }
+
+        synchronized (mKeepaliveSyncLock) {
+            mKeepalive =
+                    cm.startNattKeepalive(
+                            mConfig.getNetwork(),
+                            mConfig.getNattKeepaliveInterval(),
+                            mKeepaliveCallback,
+                            mConfig.getLocalIp(),
+                            mConfig.getEncapLocalPort(),
+                            mConfig.getRemoteIp());
+            try {
+                mKeepaliveSyncLock.wait(2000);
+            } catch (InterruptedException e) {
+            }
+        }
+        if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
+            throw new UnsupportedOperationException("Packet Keepalive cannot be started");
+        }
+    }
+
+    /* Package */
+    void stopKeepalive() {
+        if (mKeepalive == null) {
+            return;
+        }
+        mKeepalive.stop();
+        synchronized (mKeepaliveSyncLock) {
+            if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
+                try {
+                    mKeepaliveSyncLock.wait(2000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    /* Package */
+    void setTransformId(int transformId) {
+        mTransformId = transformId;
+    }
+
+    /* Package */
+    int getTransformId() {
+        return mTransformId;
+    }
+
+    /**
+     * Builder object to facilitate the creation of IpSecTransform objects.
+     *
+     * <p>Apply additional properties to the transform and then call a build() method to return an
+     * IpSecTransform object.
+     *
+     * @see Builder#buildTransportModeTransform(InetAddress)
+     */
+    public static class Builder {
+        private Context mContext;
+        private IpSecConfig mConfig;
+
+        /**
+         * Add an encryption algorithm to the transform for the given direction.
+         *
+         * <p>If encryption is set for a given direction without also providing an SPI for that
+         * direction, creation of an IpSecTransform will fail upon calling a build() method.
+         *
+         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+         * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
+         */
+        public IpSecTransform.Builder setEncryption(
+                @TransformDirection int direction, IpSecAlgorithm algo) {
+            mConfig.flow[direction].encryptionAlgo = algo;
+            return this;
+        }
+
+        /**
+         * Add an authentication/integrity algorithm to the transform.
+         *
+         * <p>If authentication is set for a given direction without also providing an SPI for that
+         * direction, creation of an IpSecTransform will fail upon calling a build() method.
+         *
+         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+         * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
+         */
+        public IpSecTransform.Builder setAuthentication(
+                @TransformDirection int direction, IpSecAlgorithm algo) {
+            mConfig.flow[direction].authenticationAlgo = algo;
+            return this;
+        }
+
+        /**
+         * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
+         * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
+         * given destination address.
+         *
+         * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
+         * possible. Random number generation is a reasonable approach to selecting an SPI. For
+         * outbound SPIs, they must be reserved by calling {@link
+         * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
+         * fail to build.
+         *
+         * <p>Unless an SPI is set for a given direction, traffic in that direction will be
+         * sent/received without any IPsec applied.
+         *
+         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+         * @param spi a unique 32-bit integer to identify transformed traffic
+         */
+        public IpSecTransform.Builder setSpi(@TransformDirection int direction, int spi) {
+            mConfig.flow[direction].spi = spi;
+            return this;
+        }
+
+        /**
+         * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
+         * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
+         * given destination address.
+         *
+         * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
+         * possible. Random number generation is a reasonable approach to selecting an SPI. For
+         * outbound SPIs, they must be reserved by calling {@link
+         * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
+         * fail to activate.
+         *
+         * <p>Unless an SPI is set for a given direction, traffic in that direction will be
+         * sent/received without any IPsec applied.
+         *
+         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+         * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+         *     traffic
+         */
+        public IpSecTransform.Builder setSpi(
+                @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
+            mConfig.flow[direction].spi = spi.getSpi();
+            return this;
+        }
+
+        /**
+         * Specify the network on which this transform will emit its traffic; (otherwise it will
+         * emit on the default network).
+         *
+         * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in
+         * tunnel mode.
+         *
+         * @hide
+         */
+        @SystemApi
+        public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
+            mConfig.network = net;
+            return this;
+        }
+
+        /**
+         * Add UDP encapsulation to an IPv4 transform
+         *
+         * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for
+         * details on how UDP should be applied to IPsec.
+         *
+         * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and
+         *     receiving encapsulating traffic.
+         * @param remotePort the UDP port number of the remote that will send and receive
+         *     encapsulated traffic. In the case of IKE, this is likely port 4500.
+         */
+        public IpSecTransform.Builder setIpv4Encapsulation(
+                IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+            // TODO: check encap type is valid.
+            mConfig.encapType = ENCAP_ESPINUDP;
+            mConfig.encapLocalPort = localSocket.getPort(); // TODO: plug in the encap socket
+            mConfig.encapRemotePort = remotePort;
+            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.
+        /**
+         * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded
+         * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot
+         * be activated, then the transform will fail to activate and throw an IOException.
+         *
+         * @param intervalSeconds the maximum number of seconds between keepalive packets, no less
+         *     than 20s and no more than 3600s.
+         * @hide
+         */
+        @SystemApi
+        public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
+            mConfig.nattKeepaliveInterval = intervalSeconds;
+            return this;
+        }
+
+        /**
+         * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform.
+         * Some parameters have interdependencies that are checked at build time. If a well-formed
+         * transform cannot be created from the supplied parameters, this method will throw an
+         * Exception.
+         *
+         * <p>Upon a successful return from this call, the provided IpSecTransform will be active
+         * and may be applied to sockets. If too many IpSecTransform objects are active for a given
+         * user this operation will fail and throw ResourceUnavailableException. To avoid these
+         * exceptions, unused Transform objects must be cleaned up by calling {@link
+         * IpSecTransform#close()} when they are no longer needed.
+         *
+         * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this
+         *     socket will cause the transform to be applied.
+         *     <p>Note that an active transform will not impact any network traffic until it has
+         *     been applied to one or more Sockets. Calling this method is a necessary precondition
+         *     for applying it to a socket, but is not sufficient to actually apply IPsec.
+         * @throws IllegalArgumentException indicating that a particular combination of transform
+         *     properties is invalid.
+         * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms
+         *     may be allocated
+         * @throws SpiUnavailableException if the SPI collides with an existing transform
+         *     (unlikely).
+         * @throws ResourceUnavailableException if the current user currently has exceeded the
+         *     number of allowed active transforms.
+         */
+        public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+                throws IpSecManager.ResourceUnavailableException,
+                        IpSecManager.SpiUnavailableException, IOException {
+            //FIXME: argument validation here
+            //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+            mConfig.mode = MODE_TRANSPORT;
+            mConfig.remoteAddress = remoteAddress;
+            return new IpSecTransform(mContext, mConfig).activate();
+        }
+
+        /**
+         * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
+         * parameters have interdependencies that are checked at build time.
+         *
+         * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+         *     IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
+         *     that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
+         * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
+         *     IPsec tunnel.
+         * @throws IllegalArgumentException indicating that a particular combination of transform
+         *     properties is invalid.
+         * @hide
+         */
+        @SystemApi
+        public IpSecTransform buildTunnelModeTransform(
+                InetAddress localAddress, InetAddress remoteAddress) {
+            //FIXME: argument validation here
+            //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+            mConfig.localAddress = localAddress;
+            mConfig.remoteAddress = remoteAddress;
+            mConfig.mode = MODE_TUNNEL;
+            return new IpSecTransform(mContext, mConfig);
+        }
+
+        /**
+         * Create a new IpSecTransform.Builder to construct an IpSecTransform
+         *
+         * @param context current Context
+         */
+        public Builder(Context context) {
+            mContext = context;
+            mConfig = new IpSecConfig();
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b1dcb81..38ad68d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6416,6 +6416,12 @@
         public static final String DEVICE_PAIRED = "device_paired";
 
         /**
+         * Specifies additional package name for broadcasting the CMAS messages.
+         * @hide
+         */
+        public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -7692,6 +7698,16 @@
         public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS =
                 "network_recommendation_request_timeout_ms";
 
+        /**
+         * The expiration time in milliseconds for the {@link android.net.WifiKey} request cache in
+         * {@link com.android.server.wifi.RecommendedNetworkEvaluator}.
+         *
+         * Type: long
+         * @hide
+         */
+        public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
+                "recommended_network_evaluator_cache_expiry_ms";
+
        /**
         * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
         * connectivity.
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index f1bc76e..dd5622d 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -14,128 +14,11 @@
  * limitations under the License.
  */
 
-#include "JNIHelp.h"
 #include "core_jni_helpers.h"
 #include "JniConstants.h"
 #include "utils/Log.h"
-#include "utils/misc.h"
-
-#if defined __arm__ || defined __aarch64__
-
-#include <vector>
-
-#include <sys/prctl.h>
-
-#include <linux/unistd.h>
-#include <linux/audit.h>
-#include <linux/filter.h>
-#include <linux/seccomp.h>
-
 #include "seccomp_policy.h"
 
-#define syscall_nr (offsetof(struct seccomp_data, nr))
-#define arch_nr (offsetof(struct seccomp_data, arch))
-
-typedef std::vector<sock_filter> filter;
-
-// We want to keep the below inline functions for debugging and future
-// development even though they are not all sed currently.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-
-static inline void Kill(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
-}
-
-static inline void Trap(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
-}
-
-static inline void Error(filter& f, __u16 retcode) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
-}
-
-inline static void Trace(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
-}
-
-inline static void Allow(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
-}
-
-#pragma clang diagnostic pop
-
-inline static void ExamineSyscall(filter& f) {
-    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
-}
-
-inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
-    size_t jump_length = f.size() - offset - 1;
-    auto u8_jump_length = (__u8) jump_length;
-    if (u8_jump_length != jump_length) {
-        ALOGE("Can't set jump greater than 255 - actual jump is %zu",
-              jump_length);
-        return -1;
-    }
-    f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, u8_jump_length, 0);
-    return 0;
-}
-
-inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
-    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
-
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_AARCH64, 2, 0));
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, 1, 0));
-    Trap(f);
-    return f.size() - 2;
-}
-
-static bool install_filter(filter const& f) {
-    struct sock_fprog prog = {
-        (unsigned short) f.size(),
-        (struct sock_filter*) &f[0],
-    };
-
-    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
-        ALOGE("SECCOMP: Could not set seccomp filter of size %zu: %s", f.size(), strerror(errno));
-        return false;
-    }
-
-    ALOGI("SECCOMP: Global filter of size %zu installed", f.size());
-    return true;
-}
-
-bool set_seccomp_filter() {
-    filter f;
-
-    // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
-    // jump that must be changed to point to the start of the 32-bit policy
-    // 32 bit syscalls will not hit the policy between here and the call to SetJump
-    auto offset_to_32bit_filter =
-        ValidateArchitectureAndJumpIfNeeded(f);
-
-    // 64-bit filter
-    ExamineSyscall(f);
-
-    // arm64-only filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm64_filter_size; ++i)
-        f.push_back(arm64_filter[i]);
-    Trap(f);
-
-    if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
-        return -1;
-
-    // 32-bit filter
-    ExamineSyscall(f);
-
-    // arm32 filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm_filter_size; ++i)
-        f.push_back(arm_filter[i]);
-    Trap(f);
-
-    return install_filter(f);
-}
-
 static void Seccomp_setPolicy(JNIEnv* /*env*/) {
     if (!set_seccomp_filter()) {
         ALOGE("Failed to set seccomp policy - killing");
@@ -143,13 +26,6 @@
     }
 }
 
-#else // #if defined __arm__ || defined __aarch64__
-
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
-}
-
-#endif
-
 static const JNINativeMethod method_table[] = {
     NATIVE_METHOD(Seccomp, setPolicy, "()V"),
 };
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index c7998a1..1c6ead0 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -111,11 +111,12 @@
 }
 
 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
-        NativeInputChannel* nativeInputChannel) {
+        std::unique_ptr<NativeInputChannel> nativeInputChannel) {
     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
             gInputChannelClassInfo.ctor);
     if (inputChannelObj) {
-        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
+                 nativeInputChannel.release());
     }
     return inputChannelObj;
 }
@@ -143,13 +144,13 @@
     }
 
     jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
-            new NativeInputChannel(serverChannel));
+            std::make_unique<NativeInputChannel>(serverChannel));
     if (env->ExceptionCheck()) {
         return NULL;
     }
 
     jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
-            new NativeInputChannel(clientChannel));
+            std::make_unique<NativeInputChannel>(clientChannel));
     if (env->ExceptionCheck()) {
         return NULL;
     }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4ff78ea..09bf39c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2683,6 +2683,6 @@
     <!-- An array of packages for which notifications cannot be blocked. -->
     <string-array translatable="false" name="config_nonBlockableNotificationPackages" />
 
-    <!-- Component name of the default cell broadcast receiver -->
-    <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
+    <!-- Package name of the default cell broadcast receiver -->
+    <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 66087d6..dbeda0b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2741,5 +2741,5 @@
 <!-- Network Recommendation -->
   <java-symbol type="array" name="config_networkRecommendationPackageNames" />
 
-  <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" />
+  <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" />
 </resources>
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3b3ce07..66576b5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -166,7 +166,7 @@
         }
 
         public String toString() {
-            return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) +
+            return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) +
                     (mEnable ? "  Enabled " : " Disabled ") + " by " + mPackageName;
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 63e66a2..1da5e69 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -950,6 +950,8 @@
         // Events from NetworkCallbacks that we process on the master state
         // machine thread on behalf of the UpstreamNetworkMonitor.
         static final int EVENT_UPSTREAM_CALLBACK                = BASE_MASTER + 5;
+        // we treated the error and want now to clear it
+        static final int CMD_CLEAR_ERROR                        = BASE_MASTER + 6;
 
         private State mInitialState;
         private State mTetherModeAliveState;
@@ -1496,6 +1498,10 @@
                         TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
                         who.sendMessage(mErrorNotification);
                         break;
+                    case CMD_CLEAR_ERROR:
+                        mErrorNotification = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+                        transitionTo(mInitialState);
+                        break;
                     default:
                        retValue = false;
                 }
@@ -1640,6 +1646,12 @@
             // Not really very much we can do here.
         }
 
+        // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
+        // Thus we give a chance for TetherMasterSM to recover to InitialState
+        // by sending CMD_CLEAR_ERROR
+        if (error == ConnectivityManager.TETHER_ERROR_MASTER_ERROR) {
+            mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
+        }
         switch (state) {
             case IControlsTethering.STATE_UNAVAILABLE:
             case IControlsTethering.STATE_AVAILABLE:
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 5e51579..710ab33 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -167,7 +167,8 @@
     private void maybeLogMessage(State state, int what) {
         if (DBG) {
             Log.d(TAG, state.getName() + " got " +
-                    sMagicDecoderRing.get(what, Integer.toString(what)));
+                    sMagicDecoderRing.get(what, Integer.toString(what)) + ", Iface = " +
+                    mIfaceName);
         }
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d67a0d6..1507082 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -871,6 +871,7 @@
      * party, if it is active.
      */
     public static final class RttCall {
+        /** @hide */
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({RTT_MODE_INVALID, RTT_MODE_FULL, RTT_MODE_HCO, RTT_MODE_VCO})
         public @interface RttAudioMode {}
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index 216603c..e0e3a08 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -235,7 +235,7 @@
         StringBuilder sb = new StringBuilder();
         sb.append("Audio");
 
-        if (isAudioOnly(videoState)) {
+        if (videoState == STATE_AUDIO_ONLY) {
             sb.append(" Only");
         } else {
             if (isTransmissionEnabled(videoState)) {
@@ -256,6 +256,9 @@
 
     /**
      * Indicates whether the video state is audio only.
+     * <p>
+     * Note: Considers only whether either both the {@link #STATE_RX_ENABLED} or
+     * {@link #STATE_TX_ENABLED} bits are off, but not {@link #STATE_PAUSED}.
      *
      * @param videoState The video state.
      * @return {@code True} if the video state is audio only, {@code false} otherwise.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 02774b3..1076afc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1127,6 +1127,14 @@
     public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
             "support_3gpp_call_forwarding_while_roaming_bool";
 
+    /**
+     * When {@code true}, the user will be notified when they attempt to place an international call
+     * when the call is placed using wifi calling.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL =
+            "notify_international_call_on_wfc_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1332,6 +1340,7 @@
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+        sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 152b868..2eba402 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1139,6 +1139,8 @@
 
     private static final String KOREA_ISO_COUNTRY_CODE = "KR";
 
+    private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
     /**
      * Breaks the given number down and formats it according to the rules
      * for the country the number is from.
@@ -1437,6 +1439,30 @@
     }
 
     /**
+     * Determines if a {@param phoneNumber} is international if dialed from
+     * {@param defaultCountryIso}.
+     *
+     * @param phoneNumber The phone number.
+     * @param defaultCountryIso The current country ISO.
+     * @return {@code true} if the number is international, {@code false} otherwise.
+     * @hide
+     */
+    public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
+        // If it starts with # or * its not international.
+        if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+            return false;
+        }
+
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        try {
+            PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+            return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
+        } catch (NumberParseException e) {
+            return false;
+        }
+    }
+
+    /**
      * Format a phone number.
      * <p>
      * If the given number doesn't have the country code, the phone will be
@@ -1459,15 +1485,25 @@
         String result = null;
         try {
             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
-            /**
-             * Need to reformat any local Korean phone numbers (when the user is in Korea) with
-             * country code to corresponding national format which would replace the leading
-             * +82 with 0.
-             */
-            if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
+
+            if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
                     (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
                     (pn.getCountryCodeSource() ==
                             PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+                /**
+                 * Need to reformat any local Korean phone numbers (when the user is in Korea) with
+                 * country code to corresponding national format which would replace the leading
+                 * +82 with 0.
+                 */
+                result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+            } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+                    pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
+                    (pn.getCountryCodeSource() ==
+                            PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+                /**
+                 * Need to reformat Japanese phone numbers (when user is in Japan) with the national
+                 * dialing format.
+                 */
                 result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
             } else {
                 result = util.formatInOriginalFormat(pn, defaultCountryIso);
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index dd03305..afff6d5 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -208,7 +208,9 @@
      *
      * @see #onOemHookRawEvent
      * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
      */
+    @Deprecated
     public static final int LISTEN_OEM_HOOK_RAW_EVENT                       = 0x00008000;
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ddf5230..20aa5ef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -780,6 +780,21 @@
             "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED";
 
     /**
+     * {@link android.telecom.Connection} event used to indicate that the InCall UI should notify
+     * the user when an international call is placed while on WFC only.
+     * <p>
+     * Used when the carrier config value
+     * {@link CarrierConfigManager#KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL} is true, the device
+     * is on WFC (VoLTE not available) and an international number is dialed.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
+            "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
+
+    /**
      * Response codes for sim activation. Activation completed successfully.
      * @hide
      */
@@ -5040,7 +5055,9 @@
      *         0 request was handled succesfully, but no response data
      *         positive value success, data length of response
      * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
      */
+    @Deprecated
     public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index fe8dbfb..f1f683c 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
@@ -41,6 +42,10 @@
 import com.android.ims.internal.IImsUt;
 import com.android.internal.annotations.VisibleForTesting;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
 /**
  * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
  * ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -94,6 +99,7 @@
         public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
                 throws RemoteException {
             synchronized (mFeatures) {
+                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature");
                 onCreateImsFeatureInternal(slotId, feature, c);
             }
         }
@@ -101,6 +107,7 @@
         @Override
         public void removeImsFeature(int slotId, int feature) throws RemoteException {
             synchronized (mFeatures) {
+                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature");
                 onRemoveImsFeatureInternal(slotId, feature);
             }
         }
@@ -108,6 +115,7 @@
         @Override
         public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
                 IImsRegistrationListener listener) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -120,6 +128,7 @@
         @Override
         public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
             synchronized (mFeatures) {
+                enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession");
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
                     feature.endSession(sessionId);
@@ -130,6 +139,7 @@
         @Override
         public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
                 throws RemoteException {
+            enforceReadPhoneStatePermission("isConnected");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -141,6 +151,7 @@
 
         @Override
         public boolean isOpened(int slotId, int featureType) throws RemoteException {
+            enforceReadPhoneStatePermission("isOpened");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -152,6 +163,7 @@
 
         @Override
         public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
+            enforceReadPhoneStatePermission("getFeatureStatus");
             int status = ImsFeature.STATE_NOT_AVAILABLE;
             synchronized (mFeatures) {
                 SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
@@ -168,6 +180,7 @@
         @Override
         public void addRegistrationListener(int slotId, int featureType,
                 IImsRegistrationListener listener) throws RemoteException {
+            enforceReadPhoneStatePermission("addRegistrationListener");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -179,6 +192,7 @@
         @Override
         public void removeRegistrationListener(int slotId, int featureType,
                 IImsRegistrationListener listener) throws RemoteException {
+            enforceReadPhoneStatePermission("removeRegistrationListener");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -190,6 +204,7 @@
         @Override
         public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
                 int callSessionType, int callType) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -202,6 +217,7 @@
         @Override
         public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
                 ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -214,6 +230,7 @@
         @Override
         public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
                 String callId) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -226,6 +243,7 @@
         @Override
         public IImsUt getUtInterface(int slotId, int featureType)
                 throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -238,6 +256,7 @@
         @Override
         public IImsConfig getConfigInterface(int slotId, int featureType)
                 throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -249,6 +268,7 @@
 
         @Override
         public void turnOnIms(int slotId, int featureType) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -259,6 +279,7 @@
 
         @Override
         public void turnOffIms(int slotId, int featureType) throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -270,6 +291,7 @@
         @Override
         public IImsEcbm getEcbmInterface(int slotId, int featureType)
                 throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -282,6 +304,7 @@
         @Override
         public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
                 throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -293,6 +316,7 @@
         @Override
         public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
                 throws RemoteException {
+            enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface");
             synchronized (mFeatures) {
                 MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
                 if (feature != null) {
@@ -329,6 +353,8 @@
         }
         ImsFeature f = makeImsFeature(slotId, featureType);
         if (f != null) {
+            f.setContext(this);
+            f.setSlotId(slotId);
             f.setImsFeatureStatusCallback(c);
             featureMap.put(featureType, f);
         }
@@ -412,6 +438,17 @@
     }
 
     /**
+     * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a
+     * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps.
+     */
+    private void enforceReadPhoneStatePermission(String fn) {
+        if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            enforceCallingOrSelfPermission(READ_PHONE_STATE, fn);
+        }
+    }
+
+    /**
      * @return An implementation of MMTelFeature that will be used by the system for MMTel
      * functionality. Must be able to handle emergency calls at any time as well.
      */
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 8d7d260..988dd58 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -17,7 +17,10 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
 import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -35,6 +38,32 @@
 
     private static final String LOG_TAG = "ImsFeature";
 
+    /**
+     * Action to broadcast when ImsService is up.
+     * Internal use only.
+     * Only defined here separately compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_UP =
+            "com.android.ims.IMS_SERVICE_UP";
+
+    /**
+     * Action to broadcast when ImsService is down.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_DOWN =
+            "com.android.ims.IMS_SERVICE_DOWN";
+
+    /**
+     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+     * A long value; the phone ID corresponding to the IMS service coming up or down.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String EXTRA_PHONE_ID = "android:phone_id";
+
     // Invalid feature value
     public static final int INVALID = -1;
     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
@@ -61,11 +90,21 @@
     private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
     private IImsFeatureStatusCallback mStatusCallback;
     private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    private Context mContext;
 
     public interface INotifyFeatureRemoved {
         void onFeatureRemoved(int slotId);
     }
 
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public void setSlotId(int slotId) {
+        mSlotId = slotId;
+    }
+
     public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
         synchronized (mRemovedListeners) {
             mRemovedListeners.add(listener);
@@ -118,6 +157,30 @@
                 Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
             }
         }
+        sendImsServiceIntent(state);
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent(@ImsState int state) {
+        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            return;
+        }
+        Intent intent;
+        switch (state) {
+            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+        mContext.sendBroadcast(intent);
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 2387ba0..682b672 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -264,15 +264,6 @@
             = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
 
     /**
-     * Activity Action: Start this activity to invoke the carrier setup app.
-     * The carrier app must be signed using a certificate that matches the UICC access rules.
-     *
-     * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP
-     * permission.</p>
-     */
-    public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP";
-
-    /**
      * <p>Broadcast Action: Indicates that the action is forbidden by network.
      * <p class="note">
      * This is for the OEM applications to understand about possible provisioning issues.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 39406a11..52d2b63 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -369,6 +369,11 @@
             connect(false);
         }
 
+        public void suspend() {
+            mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
+            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        }
+
         public void disconnect() {
             mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -1048,6 +1053,7 @@
         AVAILABLE,
         NETWORK_CAPABILITIES,
         LINK_PROPERTIES,
+        SUSPENDED,
         LOSING,
         LOST,
         UNAVAILABLE
@@ -1061,7 +1067,7 @@
             state = s; network = n; arg = o;
         }
         public String toString() {
-            return String.format("%s (%s)", state, network);
+            return String.format("%s (%s) (%s)", state, network, arg);
         }
         @Override
         public boolean equals(Object o) {
@@ -1099,11 +1105,26 @@
         }
 
         @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
+            setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
+        }
+
+        @Override
+        public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
+            setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
+        }
+
+        @Override
         public void onUnavailable() {
             setLastCallback(CallbackState.UNAVAILABLE, null, null);
         }
 
         @Override
+        public void onNetworkSuspended(Network network) {
+            setLastCallback(CallbackState.SUSPENDED, network, null);
+        }
+
+        @Override
         public void onLosing(Network network, int maxMsToLive) {
             setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
         }
@@ -1126,11 +1147,12 @@
             return cb;
         }
 
-        void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
-            CallbackInfo expected = new CallbackInfo(
-                    state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
+        CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs) {
+            final Network expectedNetwork = (agent != null) ? agent.getNetwork() : null;
+            CallbackInfo expected = new CallbackInfo(state, expectedNetwork, 0);
             CallbackInfo actual = nextCallback(timeoutMs);
             assertEquals("Unexpected callback:", expected, actual);
+
             if (state == CallbackState.LOSING) {
                 String msg = String.format(
                         "Invalid linger time value %d, must be between %d and %d",
@@ -1138,10 +1160,50 @@
                 int maxMsToLive = (Integer) actual.arg;
                 assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
             }
+
+            return actual;
         }
 
-        void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
-            expectCallback(state, mockAgent, TIMEOUT_MS);
+        CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
+            return expectCallback(state, agent, TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+            expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
+
+            final boolean HAS_DATASYNC_ON_AVAILABLE = false;
+            if (HAS_DATASYNC_ON_AVAILABLE) {
+                if (expectSuspended) {
+                    expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
+                }
+                expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+                expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+            }
+        }
+
+        void expectAvailableCallbacks(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+        }
+
+        void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+        }
+
+        void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+            expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+        }
+
+        void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+            NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
+            assertTrue(nc.hasCapability(capability));
+        }
+
+        void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+            NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
+            assertFalse(nc.hasCapability(capability));
         }
 
         void assertNoCallback() {
@@ -1178,8 +1240,8 @@
         ConditionVariable cv = waitForConnectivityBroadcasts(1);
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
-        genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1193,8 +1255,8 @@
         cv = waitForConnectivityBroadcasts(2);
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1217,8 +1279,8 @@
         // Test validated networks
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
@@ -1230,9 +1292,10 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
-        wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1268,28 +1331,32 @@
         mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
 
         mCellNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.connect(true);
         // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
         // We then get LOSING when wifi validates and cell is outscored.
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mEthernetNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+        callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+        // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mEthernetNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
 
         for (int i = 0; i < 4; i++) {
             MockNetworkAgent oldNetwork, newNetwork;
@@ -1306,7 +1373,7 @@
             callback.expectCallback(CallbackState.LOSING, oldNetwork);
             // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
             // longer lingering?
-            defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
+            defaultCallback.expectAvailableCallbacks(newNetwork);
             assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
         }
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1314,17 +1381,19 @@
         // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even
         // if the network is still up.
         mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        // We expect a notification about the capabilities change, and nothing else.
+        defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
+        defaultCallback.assertNoCallback();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Wifi no longer satisfies our listen, which is for an unmetered network.
         // But because its score is 55, it's still up (and the default network).
-        defaultCallback.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Disconnect our test networks.
         mWiFiNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
 
@@ -1340,22 +1409,22 @@
 
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);   // Score: 10
-        callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        callback.expectAvailableCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi with a score of 20.
         // Cell stays up because it would satisfy the default request if it validated.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);   // Score: 20
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi with a score of 70.
@@ -1363,31 +1432,33 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.adjustScore(50);
         mWiFiNetworkAgent.connect(false);   // Score: 70
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Tear down wifi.
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
         // it's arguably correct to linger it, since it was the default network before it validated.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1395,13 +1466,15 @@
         // If a network is lingering, and we add and remove a request from it, resume lingering.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
 
         NetworkRequest cellRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_CELLULAR).build();
@@ -1417,7 +1490,7 @@
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
 
         // Cell is now the default network. Pin it with a cell-specific request.
         noopCallback = new NetworkCallback();  // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1426,8 +1499,8 @@
         // Now connect wifi, and expect it to become the default network.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
         // The default request is lingering on cell, but nothing happens to cell, and we send no
         // callbacks for it, because it's kept up by cellRequest.
         callback.assertNoCallback();
@@ -1613,7 +1686,7 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mCellNetworkAgent.connectWithoutInternet();
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test releasing NetworkRequest disconnects cellular with MMS
         cv = mCellNetworkAgent.getDisconnectedCV();
@@ -1639,7 +1712,7 @@
         MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
         mmsNetworkAgent.connectWithoutInternet();
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mmsNetworkAgent);
+        networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
         cv = mmsNetworkAgent.getDisconnectedCV();
@@ -1665,7 +1738,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         String firstRedirectUrl = "http://example.com/firstPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
-        captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
 
         // Take down network.
@@ -1678,7 +1751,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         String secondRedirectUrl = "http://example.com/secondPath";
         mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
-        captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
 
         // Make captive portal disappear then revalidate.
@@ -1688,7 +1761,9 @@
         captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        // TODO: Investigate only sending available callbacks.
+        validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -1733,7 +1808,7 @@
         mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
-        validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         // But there should be no CaptivePortal callback.
         captivePortalCallback.assertNoCallback();
     }
@@ -1786,14 +1861,14 @@
         // Bring up cell and expect CALLBACK_AVAILABLE.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
 
         // Bring up wifi and expect CALLBACK_AVAILABLE.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         cellNetworkCallback.assertNoCallback();
-        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
 
         // Bring down cell. Expect no default network callback, since it wasn't the default.
         mCellNetworkAgent.disconnect();
@@ -1803,7 +1878,7 @@
         // Bring up cell. Expect no default network callback, since it won't be the default.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
 
         // Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -1811,28 +1886,16 @@
         mWiFiNetworkAgent.disconnect();
         cellNetworkCallback.assertNoCallback();
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
     }
 
-    private class TestRequestUpdateCallback extends TestNetworkCallback {
-        @Override
-        public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
-            setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
-        }
-
-        @Override
-        public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
-            setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
-        }
-    }
-
     @SmallTest
-    public void testRequestCallbackUpdates() throws Exception {
+    public void testAdditionalStateCallbacks() throws Exception {
         // File a network request for mobile.
-        final TestNetworkCallback cellNetworkCallback = new TestRequestUpdateCallback();
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_CELLULAR).build();
         mCm.requestNetwork(cellRequest, cellNetworkCallback);
@@ -1841,10 +1904,10 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
 
-        // We should get onAvailable().
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        // We should get onCapabilitiesChanged(), when the mobile network successfully validates.
-        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
+        // We should get onAvailable(), onCapabilitiesChanged(), and
+        // onLinkPropertiesChanged() in rapid succession. Additionally, we
+        // should get onCapabilitiesChanged() when the mobile network validates.
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
         // Update LinkProperties.
@@ -1855,20 +1918,28 @@
         cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
+        // Suspend the network.
+        mCellNetworkAgent.suspend();
+        cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+
         // Register a garden variety default network request.
-        final TestNetworkCallback dfltNetworkCallback = new TestRequestUpdateCallback();
+        final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
-        // Only onAvailable() is called; no other information is delivered.
-        dfltNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
+        // as well as onNetworkSuspended() in rapid succession.
+        dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
         dfltNetworkCallback.assertNoCallback();
 
         // Request a NetworkCapabilities update; only the requesting callback is notified.
+        // TODO: Delete this together with Connectivity{Manager,Service} code.
         mCm.requestNetworkCapabilities(dfltNetworkCallback);
         dfltNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         dfltNetworkCallback.assertNoCallback();
 
         // Request a LinkProperties update; only the requesting callback is notified.
+        // TODO: Delete this together with Connectivity{Manager,Service} code.
         mCm.requestLinkProperties(dfltNetworkCallback);
         dfltNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
@@ -1911,18 +1982,20 @@
 
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
 
         // When wifi connects, cell lingers.
-        callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        fgCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        callback.expectAvailableCallbacks(mWiFiNetworkAgent);
         callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
 
@@ -1930,7 +2003,8 @@
         mService.waitForIdle();
         int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
         fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs);
-        callback.assertNoCallback();
+        // Expect a network capabilities update sans FOREGROUND.
+        callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertFalse(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
 
@@ -1939,9 +2013,15 @@
                 .addTransportType(TRANSPORT_CELLULAR).build();
         final TestNetworkCallback cellCallback = new TestNetworkCallback();
         mCm.requestNetwork(cellRequest, cellCallback);
-        cellCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        callback.assertNoCallback();  // Because the network is already up.
+        // NOTE: This request causes the network's capabilities to change. This
+        // is currently delivered before the onAvailable() callbacks.
+        // TODO: Fix this.
+        cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
+        cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+        // Expect a network capabilities update with FOREGROUND, because the most recent
+        // request causes its state to change.
+        callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
 
@@ -1949,7 +2029,8 @@
         // lingering.
         mCm.unregisterNetworkCallback(cellCallback);
         fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
-        callback.assertNoCallback();
+        // Expect a network capabilities update sans FOREGROUND.
+        callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertFalse(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
 
@@ -1957,7 +2038,7 @@
         mWiFiNetworkAgent.disconnect();
         callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
         fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
-        fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
         mCm.unregisterNetworkCallback(callback);
@@ -2098,7 +2179,7 @@
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         testFactory.expectAddRequests(2);  // Because the cell request changes score twice.
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         testFactory.waitForNetworkRequests(2);
         assertFalse(testFactory.getMyStartRequested());  // Because the cell network outscores us.
 
@@ -2189,20 +2270,22 @@
         // Bring up validated cell.
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
         Network cellNetwork = mCellNetworkAgent.getNetwork();
 
         // Bring up validated wifi.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi.
         mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
         mCm.reportNetworkConnectivity(wifiNetwork, false);
+        defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Because avoid bad wifi is off, we don't switch to cellular.
@@ -2217,18 +2300,18 @@
         // that we switch back to cell.
         tracker.configRestrictsAvoidBadWifi = false;
         tracker.reevaluate();
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
         // Switch back to a restrictive carrier.
         tracker.configRestrictsAvoidBadWifi = true;
         tracker.reevaluate();
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
 
         // Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
         mCm.setAvoidUnvalidated(wifiNetwork);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2239,13 +2322,15 @@
         mWiFiNetworkAgent.disconnect();
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
-        validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+        validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         wifiNetwork = mWiFiNetworkAgent.getNetwork();
 
         // Fail validation on wifi and expect the dialog to appear.
         mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
         mCm.reportNetworkConnectivity(wifiNetwork, false);
+        defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
         // Simulate the user selecting "switch" and checking the don't ask again checkbox.
@@ -2253,7 +2338,7 @@
         tracker.reevaluate();
 
         // We now switch to cell.
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2264,17 +2349,17 @@
         // We switch to wifi and then to cell.
         Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
         tracker.reevaluate();
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
         Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
         tracker.reevaluate();
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
 
         // If cell goes down, we switch to wifi.
         mCellNetworkAgent.disconnect();
         defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
         validatedWifiCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2296,7 +2381,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, timeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
 
         // pass timeout and validate that UNAVAILABLE is not called
         networkCallback.assertNoCallback();
@@ -2317,7 +2402,7 @@
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         final int assertTimeoutMs = 150;
-        networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, assertTimeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
         sleepFor(20);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index adf189b..59fe1ee 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -31,8 +31,7 @@
  * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This
  * class provides functionality common to both publish and subscribe discovery sessions:
  * <ul>
- *     <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} or
- *     {@link #sendMessage(PeerHandle, int, byte[], int)} methods.
+ *     <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method.
  *     <li>Creating a network-specifier when requesting a Aware connection:
  *     {@link #createNetworkSpecifier(PeerHandle, byte[])}.
  * </ul>
@@ -62,6 +61,8 @@
      * {@link #sendMessage(PeerHandle, int, byte[], int)}.
      *
      * @return Maximum retry count when sending messages.
+     *
+     * @hide
      */
     public static int getMaxSendRetryCount() {
         return MAX_SEND_RETRY_COUNT;
@@ -163,6 +164,8 @@
      *            or MAC level) retries should be attempted if there is no ACK from the receiver
      *            (note: no retransmissions are attempted in other failure cases). A value of 0
      *            indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
+     *
+     * @hide
      */
     public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
             @Nullable byte[] message, int retryCount) {
@@ -195,8 +198,6 @@
      * The peer will get a callback indicating a message was received using
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}.
-     * Equivalent to {@link #sendMessage(PeerHandle, int, byte[], int)}
-     * with a {@code retryCount} of 0.
      *
      * @param peerHandle The peer's handle for the message. Must be a result of an
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -246,8 +247,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
-     * WiFi Aware connection to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+     * unencrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -255,7 +256,58 @@
      * discovery or communication (in such scenarios the MAC address of the peer is shielded by
      * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
      * OOB (out-of-band) mechanism then use the alternative
-     * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
+     * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the
+     * peer's MAC address.
+     * <p>
+     * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+     * and a Publisher is a RESPONDER.
+     *
+     * @param peerHandle The peer's handle obtained through
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
+     *                   or
+     *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
+     *                   On a RESPONDER this value is used to gate the acceptance of a connection
+     *                   request from only that peer. A RESPONDER may specify a null - indicating
+     *                   that it will accept connection requests from any device.
+     *
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
+     *
+     * @hide
+     */
+    public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+        if (mTerminated) {
+            Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
+            return null;
+        } else {
+            WifiAwareManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
+                return null;
+            }
+
+            int role = this instanceof SubscribeDiscoverySession
+                    ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                    : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+            return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null);
+        }
+    }
+
+    /**
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+     * <p>
+     * This method should be used when setting up a connection with a peer discovered through Aware
+     * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+     * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
+     * OOB (out-of-band) mechanism then use the alternative
+     * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses the
      * peer's MAC address.
      * <p>
      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
@@ -266,29 +318,34 @@
      * byte[], java.util.List)} or
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
-     *                   from only that peer. A RESPONDER may specified a null - indicating that
+     *                   from only that peer. A RESPONDER may specify a null - indicating that
      *                   it will accept connection requests from any device.
-     * @param token An arbitrary token (message) to be used to match connection initiation request
-     *              to a responder setup. A RESPONDER is set up with a {@code token} which must
-     *              be matched by the token provided by the INITIATOR. A null token is permitted
-     *              on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
-     *              not the same as a null token and requires the peer token to be empty as well.
+     * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
+     *            encrypting the data-path. Use the
+     *            {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an open (unencrypted)
+     *            link.
      *
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
+     *
+     * @hide
      */
-    public String createNetworkSpecifier(@Nullable PeerHandle peerHandle,
-            @Nullable byte[] token) {
+    public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+            @NonNull byte[] pmk) {
+        if (pmk == null || pmk.length == 0) {
+            throw new IllegalArgumentException("PMK must not be null or empty");
+        }
+
         if (mTerminated) {
-            Log.w(TAG, "createNetworkSpecifier: called on terminated session");
+            Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session");
             return null;
         } else {
             WifiAwareManager mgr = mMgr.get();
             if (mgr == null) {
-                Log.w(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
+                Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
                 return null;
             }
 
@@ -296,7 +353,30 @@
                     ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                     : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
 
-            return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token);
+            return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk);
         }
     }
+
+    /**
+     * Place-holder for {@code createNetworkSpecifierOpen(PeerHandle)}. Present to enable
+     * development of replacements CL without causing an API change. Will be removed when new
+     * APIs are exposed.
+     *
+     * @param peerHandle The peer's handle obtained through
+     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
+     * byte[], java.util.List)} or
+     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
+     * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
+     *                   from only that peer. A RESPONDER may specify a null - indicating that
+     *                   it will accept connection requests from any device.
+     * @param token Deprecated and ignored.
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
+     */
+    public String createNetworkSpecifier(@Nullable PeerHandle peerHandle, @Nullable byte[] token) {
+        return createNetworkSpecifierOpen(peerHandle);
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 33da182..9645b1d 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -124,10 +124,9 @@
     }
 
     /**
-     * Called when message transmission fails - when no ACK is received from the peer.
-     * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
-     * the {@link DiscoverySession#sendMessage(PeerHandle, int,
-     * byte[], int)} method) - this event is received after all retries are exhausted.
+     * Called when message transmission initiated with
+     * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} fails. E.g. when no ACK is
+     * received from the peer.
      * <p>
      * Note that either this callback or
      * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)} will be received
@@ -141,9 +140,7 @@
 
     /**
      * Called when a message is received from a discovery session peer - in response to the
-     * peer's {@link DiscoverySession#sendMessage(PeerHandle, int,
-     * byte[])} or {@link DiscoverySession#sendMessage(PeerHandle,
-     * int, byte[], int)}.
+     * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}.
      *
      * @param peerHandle An opaque handle to the peer matching our discovery operation.
      * @param message A byte array containing the message.
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 0eb6a3d..3d784ba 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -130,55 +130,34 @@
      */
 
     /**
-     * TYPE_1A: role, client_id, session_id, peer_id, token
+     * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk optional
      * @hide
      */
-    public static final int NETWORK_SPECIFIER_TYPE_1A = 0;
+    public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
 
     /**
-     * TYPE_1B: role, client_id, session_id, peer_id [only permitted for RESPONDER]
+     * TYPE: in band, any peer: role, client_id, session_id, pmk optional
+     * [only permitted for RESPONDER]
      * @hide
      */
-    public static final int NETWORK_SPECIFIER_TYPE_1B = 1;
+    public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
 
     /**
-     * TYPE_1C: role, client_id, session_id, token [only permitted for RESPONDER]
+     * TYPE: out-of-band: role, client_id, peer_mac, pmk optional
      * @hide
      */
-    public static final int NETWORK_SPECIFIER_TYPE_1C = 2;
+    public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
 
     /**
-     * TYPE_1C: role, client_id, session_id [only permitted for RESPONDER]
+     * TYPE: out-of-band, any peer: role, client_id, pmk optional
+     * [only permitted for RESPONDER]
      * @hide
      */
-    public static final int NETWORK_SPECIFIER_TYPE_1D = 3;
+    public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
 
-    /**
-     * TYPE_2A: role, client_id, peer_mac, token
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_2A = 4;
-
-    /**
-     * TYPE_2B: role, client_id, peer_mac [only permitted for RESPONDER]
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_2B = 5;
-
-    /**
-     * TYPE_2C: role, client_id, token [only permitted for RESPONDER]
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_2C = 6;
-
-    /**
-     * TYPE_2D: role, client_id [only permitted for RESPONDER]
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_2D = 7;
 
     /** @hide */
-    public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_2D;
+    public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
 
     /** @hide */
     public static final String NETWORK_SPECIFIER_KEY_TYPE = "type";
@@ -199,7 +178,7 @@
     public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac";
 
     /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token";
+    public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk";
 
     /**
      * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
@@ -494,23 +473,15 @@
 
     /** @hide */
     public String createNetworkSpecifier(int clientId, int role, int sessionId,
-            PeerHandle peerHandle, byte[] token) {
+            PeerHandle peerHandle, @Nullable byte[] pmk) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
                     + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
-                    + ", token=" + token);
+                    + ", pmk=" + ((pmk == null) ? "null" : "non-null"));
         }
 
-        int type;
-        if (token != null && peerHandle != null) {
-            type = NETWORK_SPECIFIER_TYPE_1A;
-        } else if (token == null && peerHandle != null) {
-            type = NETWORK_SPECIFIER_TYPE_1B;
-        } else if (token != null && peerHandle == null) {
-            type = NETWORK_SPECIFIER_TYPE_1C;
-        } else {
-            type = NETWORK_SPECIFIER_TYPE_1D;
-        }
+        int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
+                : NETWORK_SPECIFIER_TYPE_IB;
 
         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
@@ -519,10 +490,6 @@
                             + "specifier");
         }
         if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
-            if (token == null) {
-                throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
-            }
             if (peerHandle == null) {
                 throw new IllegalArgumentException(
                         "createNetworkSpecifier: Invalid peer handle (value of null) - not "
@@ -540,10 +507,11 @@
             if (peerHandle != null) {
                 json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId);
             }
-            if (token != null) {
-                json.put(NETWORK_SPECIFIER_KEY_TOKEN,
-                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+            if (pmk == null) {
+                pmk = new byte[0];
             }
+            json.put(NETWORK_SPECIFIER_KEY_PMK,
+                    Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
         } catch (JSONException e) {
             return "";
         }
@@ -553,21 +521,14 @@
 
     /** @hide */
     public String createNetworkSpecifier(int clientId, @DataPathRole int role,
-            @Nullable byte[] peer, @Nullable byte[] token) {
+            @Nullable byte[] peer, @Nullable byte[] pmk) {
         if (VDBG) {
-            Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token);
+            Log.v(TAG, "createNetworkSpecifier: role=" + role
+                    + ", pmk=" + ((pmk == null) ? "null" : "non-null"));
         }
 
-        int type;
-        if (token != null && peer != null) {
-            type = NETWORK_SPECIFIER_TYPE_2A;
-        } else if (token == null && peer != null) {
-            type = NETWORK_SPECIFIER_TYPE_2B;
-        } else if (token != null && peer == null) {
-            type = NETWORK_SPECIFIER_TYPE_2C;
-        } else { // both are null
-            type = NETWORK_SPECIFIER_TYPE_2D;
-        }
+        int type = (peer == null) ?
+                NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB;
 
         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
@@ -576,19 +537,13 @@
                             + "specifier");
         }
         if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
-            if (peer == null || peer.length != 6) {
-                throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid peer MAC address");
+            if (peer == null) {
+                throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC "
+                        + "address - null not permitted on INITIATOR");
             }
-            if (token == null) {
-                throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
-            }
-        } else {
-            if (peer != null && peer.length != 6) {
-                throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid peer MAC address");
-            }
+        }
+        if (peer != null && peer.length != 6) {
+            throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
         }
 
         JSONObject json;
@@ -600,10 +555,11 @@
             if (peer != null) {
                 json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer)));
             }
-            if (token != null) {
-                json.put(NETWORK_SPECIFIER_KEY_TOKEN,
-                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+            if (pmk == null) {
+                pmk = new byte[0];
             }
+            json.put(NETWORK_SPECIFIER_KEY_PMK,
+                    Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
         } catch (JSONException e) {
             return "";
         }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 8696920..856066e 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -183,47 +183,114 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
-     * WiFi Aware connection to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+     * unencrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
      *     This API is targeted for applications which can obtain the peer MAC address using OOB
      *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
      *     when using Aware discovery use the alternative network specifier method -
-     *     {@link DiscoverySession#createNetworkSpecifier(PeerHandle,
-     *     byte[])}.
+     *     {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}.
      *
      * @param role  The role of this device:
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specified a null - indicating that it will accept
+     *              peer. A RESPONDER may specify a null - indicating that it will accept
      *              connection requests from any device.
-     * @param token An arbitrary token (message) to be used to match connection initiation request
-     *              to a responder setup. A RESPONDER is set up with a {@code token} which must
-     *              be matched by the token provided by the INITIATOR. A null token is permitted
-     *              on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
-     *              not the same as a null token and requires the peer token to be empty as well.
      *
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
+     *
+     * @hide
+     */
+    public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role,
+            @Nullable byte[] peer) {
+        WifiAwareManager mgr = mMgr.get();
+        if (mgr == null) {
+            Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
+            return "";
+        }
+        if (mTerminated) {
+            Log.e(TAG, "createNetworkSpecifierOpen: called after termination");
+            return "";
+        }
+        return mgr.createNetworkSpecifier(mClientId, role, peer, null);
+    }
+
+    /**
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+     * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+     * <p>
+     *     This API is targeted for applications which can obtain the peer MAC address using OOB
+     *     (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+     *     when using Aware discovery use the alternative network specifier method -
+     *     {@link DiscoverySession#createNetworkSpecifierPmk(PeerHandle, byte[])}}.
+     *
+     * @param role  The role of this device:
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+     * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
+     *              value is used to gate the acceptance of a connection request from only that
+     *              peer. A RESPONDER may specify a null - indicating that it will accept
+     *              connection requests from any device.
+     * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
+     *            encrypting the data-path. Use the {@link #createNetworkSpecifierOpen(int, byte[])}
+     *            to specify an open (unencrypted) link.
+     *
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
+     *
+     * @hide
+     */
+    public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role,
+            @Nullable byte[] peer, @NonNull byte[] pmk) {
+        WifiAwareManager mgr = mMgr.get();
+        if (mgr == null) {
+            Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
+            return "";
+        }
+        if (mTerminated) {
+            Log.e(TAG, "createNetworkSpecifierPmk: called after termination");
+            return "";
+        }
+        if (pmk == null || pmk.length == 0) {
+            throw new IllegalArgumentException("PMK must not be null or empty");
+        }
+        return mgr.createNetworkSpecifier(mClientId, role, peer, pmk);
+    }
+
+    /**
+     * Place-holder for {@code #createNetworkSpecifierOpen(int, byte[])}. Present to enable
+     * development of replacements CL without causing an API change. Will be removed when new
+     * APIs are exposed.
+     *
+     * @param role  The role of this device:
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+     *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+     * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
+     *              value is used to gate the acceptance of a connection request from only that
+     *              peer. A RESPONDER may specify a null - indicating that it will accept
+     *              connection requests from any device.
+     * @param token Deprecated and ignored.
+     * @return A string to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+     * android.net.ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
      */
     public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
             @Nullable byte[] peer, @Nullable byte[] token) {
-        WifiAwareManager mgr = mMgr.get();
-        if (mgr == null) {
-            Log.e(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
-            return "";
-        }
-        if (mTerminated) {
-            Log.e(TAG, "createNetworkSpecifier: called after termination");
-            return "";
-        }
-        return mgr.createNetworkSpecifier(mClientId, role, peer, token);
+        return createNetworkSpecifierOpen(role, peer);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 7f68f6f..992958b 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -973,11 +973,11 @@
         final int sessionId = 123;
         final PeerHandle peerHandle = new PeerHandle(123412);
         final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
-        final String token = "Some arbitrary token string - can really be anything";
+        final byte[] pmk = "Some arbitrary byte array".getBytes();
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
-        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+        String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
 
         ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
                 WifiAwareSession.class);
@@ -1008,9 +1008,8 @@
         mMockLooper.dispatchAll();
         inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
 
-        // (3) request a network specifier from the session
-        String networkSpecifier = publishSession.getValue().createNetworkSpecifier(peerHandle,
-                token.getBytes());
+        // (3) request an open (unencrypted) network specifier from the session
+        String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle);
 
         // validate format
         JSONObject jsonObject = new JSONObject(networkSpecifier);
@@ -1022,8 +1021,22 @@
                 equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
         collector.checkThat("peer_id", peerHandle.peerId,
                 equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
-        collector.checkThat("token", tokenB64,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        // (4) request an encrypted (PMK) network specifier from the session
+        networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+
+        // validate format
+        jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("session_id", sessionId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+        collector.checkThat("peer_id", peerHandle.peerId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+        collector.checkThat("pmk", pmkB64 ,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);
@@ -1039,9 +1052,9 @@
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
         final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
-        final String token = "Some arbitrary token string - can really be anything";
+        final byte[] pmk = "Some arbitrary pmk data".getBytes();
 
-        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+        String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
 
         ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
                 WifiAwareSession.class);
@@ -1060,10 +1073,10 @@
         inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
         WifiAwareSession session = sessionCaptor.getValue();
 
-        /* (2) request a direct network specifier*/
-        String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes());
+        // (2) request an open (unencrypted) direct network specifier
+        String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac);
 
-        /* validate format*/
+        // validate format
         JSONObject jsonObject = new JSONObject(networkSpecifier);
         collector.checkThat("role", role,
                 equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
@@ -1072,8 +1085,21 @@
         collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
                 jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
                 false)));
-        collector.checkThat("token", tokenB64,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        // (3) request an encrypted (PMK) direct network specifier
+        networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk);
+
+        // validate format
+        jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+                false)));
+        collector.checkThat("pmk", pmkB64,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);