Merge "Fix documentation generation with OpenJDK 9 javadoc."
diff --git a/Android.bp b/Android.bp
index 6b61467..0b09b46 100644
--- a/Android.bp
+++ b/Android.bp
@@ -16,8 +16,6 @@
 cc_library {
     name: "libplatformprotos",
     host_supported: true,
-    // b/34740546, work around clang-tidy segmentation fault.
-    tidy_checks: ["-modernize*"],
     proto: {
         export_proto_headers: true,
         include_dirs: ["external/protobuf/src"],
@@ -63,3 +61,25 @@
 optional_subdirs = [
     "core/tests/utiltests/jni",
 ]
+
+java_library {
+    name: "hwbinder",
+    no_framework_libs: true,
+
+    srcs: [
+        "core/java/android/os/HidlSupport.java",
+        "core/java/android/annotation/NonNull.java",
+        "core/java/android/os/HwBinder.java",
+        "core/java/android/os/HwBlob.java",
+        "core/java/android/os/HwParcel.java",
+        "core/java/android/os/IHwBinder.java",
+        "core/java/android/os/IHwInterface.java",
+        "core/java/android/os/DeadObjectException.java",
+        "core/java/android/os/DeadSystemException.java",
+        "core/java/android/os/RemoteException.java",
+        "core/java/android/util/AndroidException.java",
+    ],
+
+    dxflags: ["--core-library"],
+    installable: false,
+}
diff --git a/Android.mk b/Android.mk
index b92f31e..d9a175c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -611,32 +611,6 @@
 
 framework_built := $(call java-lib-deps,framework)
 
-# HwBinder
-# =======================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-        core/java/android/os/HidlSupport.java \
-        core/java/android/annotation/NonNull.java \
-        core/java/android/os/HwBinder.java \
-        core/java/android/os/HwBlob.java \
-        core/java/android/os/HwParcel.java \
-        core/java/android/os/IHwBinder.java \
-        core/java/android/os/IHwInterface.java \
-        core/java/android/os/DeadObjectException.java \
-        core/java/android/os/DeadSystemException.java \
-        core/java/android/os/RemoteException.java \
-        core/java/android/util/AndroidException.java \
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := hwbinder
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_JAVA_LIBRARY)
-
 # Copy AIDL files to be preprocessed and included in the SDK,
 # specified relative to the root of the build tree.
 # ============================================================
diff --git a/api/current.txt b/api/current.txt
index c1f05e5..df4f8b7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40510,8 +40510,13 @@
 
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
+    ctor public DownloadStateCallback(int);
+    method public final boolean isFilterFlagSet(int);
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
     method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    field public static final int ALL_UPDATES = 0; // 0x0
+    field public static final int PROGRESS_UPDATES = 1; // 0x1
+    field public static final int STATE_UPDATES = 2; // 0x2
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -40585,6 +40590,7 @@
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
     method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+    method public java.util.Set<java.util.Locale> getNamedContentLocales();
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
@@ -52139,6 +52145,8 @@
     field public static final int OP_CONST_CLASS = 28; // 0x1c
     field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
     field public static final int OP_CONST_HIGH16 = 21; // 0x15
+    field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+    field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
     field public static final int OP_CONST_STRING = 26; // 0x1a
     field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
     field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/api/system-current.txt b/api/system-current.txt
index 0ce7908..16fa870 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -44029,8 +44029,13 @@
 
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
+    ctor public DownloadStateCallback(int);
+    method public final boolean isFilterFlagSet(int);
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
     method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    field public static final int ALL_UPDATES = 0; // 0x0
+    field public static final int PROGRESS_UPDATES = 1; // 0x1
+    field public static final int STATE_UPDATES = 2; // 0x2
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -44113,6 +44118,7 @@
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
     method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+    method public java.util.Set<java.util.Locale> getNamedContentLocales();
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
@@ -56119,6 +56125,8 @@
     field public static final int OP_CONST_CLASS = 28; // 0x1c
     field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
     field public static final int OP_CONST_HIGH16 = 21; // 0x15
+    field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+    field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
     field public static final int OP_CONST_STRING = 26; // 0x1a
     field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
     field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/api/test-current.txt b/api/test-current.txt
index 685e06a..cf8b8b3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -40732,8 +40732,13 @@
 
   public class DownloadStateCallback {
     ctor public DownloadStateCallback();
+    ctor public DownloadStateCallback(int);
+    method public final boolean isFilterFlagSet(int);
     method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
     method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+    field public static final int ALL_UPDATES = 0; // 0x0
+    field public static final int PROGRESS_UPDATES = 1; // 0x1
+    field public static final int STATE_UPDATES = 2; // 0x2
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -40807,6 +40812,7 @@
   public class ServiceInfo {
     method public java.util.List<java.util.Locale> getLocales();
     method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+    method public java.util.Set<java.util.Locale> getNamedContentLocales();
     method public java.lang.String getServiceClassName();
     method public java.lang.String getServiceId();
     method public java.util.Date getSessionEndTime();
@@ -52563,6 +52569,8 @@
     field public static final int OP_CONST_CLASS = 28; // 0x1c
     field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
     field public static final int OP_CONST_HIGH16 = 21; // 0x15
+    field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+    field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
     field public static final int OP_CONST_STRING = 26; // 0x1a
     field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
     field public static final int OP_CONST_WIDE = 24; // 0x18
diff --git a/compiled-classes-phone b/config/compiled-classes-phone
similarity index 99%
rename from compiled-classes-phone
rename to config/compiled-classes-phone
index 71fc5c1..063276d 100644
--- a/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -26,7 +26,7 @@
 # to your device.mk, e.g.,
 #
 #   PRODUCT_COPY_FILES += \
-#     frameworks/base/compiled-classes-phone:system/etc/compiled-classes
+#     frameworks/base/config/compiled-classes-phone:system/etc/compiled-classes
 #
 android.R$styleable
 android.accessibilityservice.AccessibilityServiceInfo
diff --git a/dirty-image-objects b/config/dirty-image-objects
similarity index 100%
rename from dirty-image-objects
rename to config/dirty-image-objects
diff --git a/preloaded-classes b/config/preloaded-classes
similarity index 99%
rename from preloaded-classes
rename to config/preloaded-classes
index c1896aa..8233b03 100644
--- a/preloaded-classes
+++ b/config/preloaded-classes
@@ -2286,6 +2286,7 @@
 android.util.Log
 android.util.Log$1
 android.util.Log$ImmediateLogWriter
+android.util.Log$PreloadHolder
 android.util.Log$TerribleFailure
 android.util.Log$TerribleFailureHandler
 android.util.LogPrinter
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 70591d4..84765f6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1134,6 +1134,29 @@
     }
 
     /**
+     * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of
+     * the local Bluetooth adapter.
+     *
+     * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
+     * @return true if successful, false if unsuccessful.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
+        if (getState() != STATE_ON) return false;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.setBluetoothClass(bluetoothClass);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
+    }
+
+    /**
      * Get the current Bluetooth scan mode of the local Bluetooth adapter.
      * <p>The Bluetooth scan mode determines if the local adapter is
      * connectable and/or discoverable from remote Bluetooth devices.
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 57e4abb..f22ea6e 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -19,6 +19,10 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
 /**
  * Represents a Bluetooth class, which describes general characteristics
  * and capabilities of a device. For example, a Bluetooth class will
@@ -275,6 +279,48 @@
         return (mClass & Device.BITMASK);
     }
 
+    /**
+     * Return the Bluetooth Class of Device (CoD) value including the
+     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
+     * minor device fields.
+     *
+     * <p>This value is an integer representation of Bluetooth CoD as in
+     * Bluetooth specification.
+     *
+     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
+     *
+     * @hide
+     */
+    public int getClassOfDevice() {
+        return mClass;
+    }
+
+    /**
+     * Return the Bluetooth Class of Device (CoD) value including the
+     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
+     * minor device fields.
+     *
+     * <p>This value is a byte array representation of Bluetooth CoD as in
+     * Bluetooth specification.
+     *
+     * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the
+     * MSB is useless and needs to be thrown away. The lower 3 bytes are
+     * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN.
+     *
+     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
+     *
+     * @hide
+     */
+    public byte[] getClassOfDeviceBytes() {
+        byte[] bytes = ByteBuffer.allocate(4)
+                .order(ByteOrder.BIG_ENDIAN)
+                .putInt(mClass)
+                .array();
+
+        // Discard the top byte
+        return Arrays.copyOfRange(bytes, 1, bytes.length);
+    }
+
     /** @hide */
     public static final int PROFILE_HEADSET = 0;
     /** @hide */
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
index 37f0427..e18d9d1 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -74,7 +74,7 @@
     public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
     public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
     public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
-    public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05;
+    public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
     public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 19f5198..a1a9347 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.SdkConstant;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -53,35 +54,32 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    /** int extra for PBAP_STATE_CHANGED_ACTION */
-    public static final String PBAP_STATE =
-            "android.bluetooth.pbap.intent.PBAP_STATE";
-    /** int extra for PBAP_STATE_CHANGED_ACTION */
-    public static final String PBAP_PREVIOUS_STATE =
-            "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
-
     /**
-     * Indicates the state of a pbap connection state has changed.
-     * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
-     * BluetoothIntent.ADDRESS extras.
+     * Intent used to broadcast the change in connection state of the PBAP
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
+     *  can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
+     *  {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
+     *  {@link BluetoothProfile#STATE_DISCONNECTING}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
      */
-    public static final String PBAP_STATE_CHANGED_ACTION =
-            "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
 
     private volatile IBluetoothPbap mService;
     private final Context mContext;
     private ServiceListener mServiceListener;
     private BluetoothAdapter mAdapter;
 
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR = -1;
-    /** No client currently connected */
-    public static final int STATE_DISCONNECTED = 0;
-    /** Connection attempt in progress */
-    public static final int STATE_CONNECTING = 1;
-    /** Client is currently connected */
-    public static final int STATE_CONNECTED = 2;
-
     public static final int RESULT_FAILURE = 0;
     public static final int RESULT_SUCCESS = 1;
     /** Connection canceled before completion. */
@@ -209,8 +207,8 @@
     /**
      * Get the current state of the BluetoothPbap service.
      *
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
-     * connected to the Pbap service.
+     * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED}
+     * if this proxy object is currently not connected to the Pbap service.
      */
     public int getState() {
         if (VDBG) log("getState()");
@@ -225,7 +223,7 @@
             Log.w(TAG, "Proxy not attached to service");
             if (DBG) log(Log.getStackTraceString(new Throwable()));
         }
-        return BluetoothPbap.STATE_ERROR;
+        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 00a15f3..01b3f6e 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -40,7 +40,7 @@
     private static final boolean VDBG = false;
 
     public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+            "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
 
     private volatile IBluetoothPbapClient mService;
     private final Context mContext;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 583cf32..0ccebc1c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2084,16 +2084,30 @@
      * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
      * due to device configuration.
      *
+     * <p>If this app does not have permission to use this API, it will always
+     * return false rather than throw an exception.</p>
+     *
+     * <p>If the device has a hotspot provisioning app, the caller is required to hold the
+     * {@link android.Manifest.permission.TETHER_PRIVILEGED} permission.</p>
+     *
+     * <p>Otherwise, this method requires the caller to hold the ability to modify system
+     * settings as determined by {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @return a boolean - {@code true} indicating Tethering is supported.
      *
      * {@hide}
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+    @RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
+            android.Manifest.permission.WRITE_SETTINGS})
     public boolean isTetheringSupported() {
+        String pkgName = mContext.getOpPackageName();
         try {
-            String pkgName = mContext.getOpPackageName();
             return mService.isTetheringSupported(pkgName);
+        } catch (SecurityException e) {
+            // This API is not available to this caller, but for backward-compatibility
+            // this will just return false instead of throwing.
+            return false;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index ead406c..79310e2 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
@@ -75,13 +76,7 @@
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
     /** @hide */
-    @StringDef({
-        CRYPT_AES_CBC,
-        AUTH_HMAC_MD5,
-        AUTH_HMAC_SHA1,
-        AUTH_HMAC_SHA256,
-        AUTH_HMAC_SHA512
-    })
+    @StringDef({CRYPT_AES_CBC, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA512})
     @Retention(RetentionPolicy.SOURCE)
     public @interface AlgorithmName {}
 
@@ -197,4 +192,12 @@
                 .append("}")
                 .toString();
     }
+
+    /** package */
+    static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
+        if (lhs == null || rhs == null) return (lhs == rhs);
+        return (lhs.mName.equals(rhs.mName)
+                && Arrays.equals(lhs.mKey, rhs.mKey)
+                && lhs.mTruncLenBits == rhs.mTruncLenBits);
+    }
 };
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 5a5c740..632b7fc 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -17,105 +17,170 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 /** @hide */
 public final class IpSecConfig implements Parcelable {
     private static final String TAG = "IpSecConfig";
 
-    //MODE_TRANSPORT or MODE_TUNNEL
-    int mode;
+    // MODE_TRANSPORT or MODE_TUNNEL
+    private int mMode = IpSecTransform.MODE_TRANSPORT;
 
-    // For tunnel mode
-    InetAddress localAddress;
+    // Needs to be valid only for tunnel mode
+    // Preventing this from being null simplifies Java->Native binder
+    private String mLocalAddress = "";
 
-    InetAddress remoteAddress;
+    // Preventing this from being null simplifies Java->Native binder
+    private String mRemoteAddress = "";
 
-    // Limit selection by network interface
-    Network network;
+    // The underlying Network that represents the "gateway" Network
+    // for outbound packets. It may also be used to select packets.
+    private Network mNetwork;
 
     public static class Flow {
         // Minimum requirements for identifying a transform
         // SPI identifying the IPsec flow in packet processing
         // and a remote IP address
-        int spiResourceId;
+        private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
 
         // Encryption Algorithm
-        IpSecAlgorithm encryption;
+        private IpSecAlgorithm mEncryption;
 
         // Authentication Algorithm
-        IpSecAlgorithm authentication;
+        private IpSecAlgorithm mAuthentication;
 
         @Override
         public String toString() {
             return new StringBuilder()
-                    .append("{spiResourceId=")
-                    .append(spiResourceId)
-                    .append(", encryption=")
-                    .append(encryption)
-                    .append(", authentication=")
-                    .append(authentication)
+                    .append("{mSpiResourceId=")
+                    .append(mSpiResourceId)
+                    .append(", mEncryption=")
+                    .append(mEncryption)
+                    .append(", mAuthentication=")
+                    .append(mAuthentication)
                     .append("}")
                     .toString();
         }
+
+        static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
+            if (lhs == null || rhs == null) return (lhs == rhs);
+            return (lhs.mSpiResourceId == rhs.mSpiResourceId
+                    && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+                    && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
+        }
     }
 
-    final Flow[] flow = new Flow[] {new Flow(), new Flow()};
+    private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
 
     // For tunnel mode IPv4 UDP Encapsulation
     // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
-    int encapType;
-    int encapLocalPortResourceId;
-    int encapRemotePort;
+    private int mEncapType = IpSecTransform.ENCAP_NONE;
+    private int mEncapSocketResourceId = IpSecManager.INVALID_RESOURCE_ID;
+    private int mEncapRemotePort;
 
     // An interval, in seconds between the NattKeepalive packets
-    int nattKeepaliveInterval;
+    private int mNattKeepaliveInterval;
+
+    /** Set the mode for this IPsec transform */
+    public void setMode(int mode) {
+        mMode = mode;
+    }
+
+    /** Set the local IP address for Tunnel mode */
+    public void setLocalAddress(String localAddress) {
+        if (localAddress == null) {
+            throw new IllegalArgumentException("localAddress may not be null!");
+        }
+        mLocalAddress = localAddress;
+    }
+
+    /** Set the remote IP address for this IPsec transform */
+    public void setRemoteAddress(String remoteAddress) {
+        if (remoteAddress == null) {
+            throw new IllegalArgumentException("remoteAddress may not be null!");
+        }
+        mRemoteAddress = remoteAddress;
+    }
+
+    /** Set the SPI for a given direction by resource ID */
+    public void setSpiResourceId(int direction, int resourceId) {
+        mFlow[direction].mSpiResourceId = resourceId;
+    }
+
+    /** Set the encryption algorithm for a given direction */
+    public void setEncryption(int direction, IpSecAlgorithm encryption) {
+        mFlow[direction].mEncryption = encryption;
+    }
+
+    /** Set the authentication algorithm for a given direction */
+    public void setAuthentication(int direction, IpSecAlgorithm authentication) {
+        mFlow[direction].mAuthentication = authentication;
+    }
+
+    public void setNetwork(Network network) {
+        mNetwork = network;
+    }
+
+    public void setEncapType(int encapType) {
+        mEncapType = encapType;
+    }
+
+    public void setEncapSocketResourceId(int resourceId) {
+        mEncapSocketResourceId = resourceId;
+    }
+
+    public void setEncapRemotePort(int port) {
+        mEncapRemotePort = port;
+    }
+
+    public void setNattKeepaliveInterval(int interval) {
+        mNattKeepaliveInterval = interval;
+    }
 
     // Transport or Tunnel
     public int getMode() {
-        return mode;
+        return mMode;
     }
 
-    public InetAddress getLocalAddress() {
-        return localAddress;
+    public String getLocalAddress() {
+        return mLocalAddress;
     }
 
     public int getSpiResourceId(int direction) {
-        return flow[direction].spiResourceId;
+        return mFlow[direction].mSpiResourceId;
     }
 
-    public InetAddress getRemoteAddress() {
-        return remoteAddress;
+    public String getRemoteAddress() {
+        return mRemoteAddress;
     }
 
     public IpSecAlgorithm getEncryption(int direction) {
-        return flow[direction].encryption;
+        return mFlow[direction].mEncryption;
     }
 
     public IpSecAlgorithm getAuthentication(int direction) {
-        return flow[direction].authentication;
+        return mFlow[direction].mAuthentication;
     }
 
     public Network getNetwork() {
-        return network;
+        return mNetwork;
     }
 
     public int getEncapType() {
-        return encapType;
+        return mEncapType;
     }
 
-    public int getEncapLocalResourceId() {
-        return encapLocalPortResourceId;
+    public int getEncapSocketResourceId() {
+        return mEncapSocketResourceId;
     }
 
     public int getEncapRemotePort() {
-        return encapRemotePort;
+        return mEncapRemotePort;
     }
 
     public int getNattKeepaliveInterval() {
-        return nattKeepaliveInterval;
+        return mNattKeepaliveInterval;
     }
 
     // Parcelable Methods
@@ -127,82 +192,70 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        // 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].spiResourceId);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags);
-        out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spiResourceId);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags);
-        out.writeInt(encapType);
-        out.writeInt(encapLocalPortResourceId);
-        out.writeInt(encapRemotePort);
+        out.writeInt(mMode);
+        out.writeString(mLocalAddress);
+        out.writeString(mRemoteAddress);
+        out.writeParcelable(mNetwork, flags);
+        out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
+        out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
+        out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
+        out.writeInt(mEncapType);
+        out.writeInt(mEncapSocketResourceId);
+        out.writeInt(mEncapRemotePort);
+        out.writeInt(mNattKeepaliveInterval);
     }
 
-    // Package Private: Used by the IpSecTransform.Builder;
-    // there should be no public constructor for this object
-    IpSecConfig() {}
-
-    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;
-        }
-    }
+    @VisibleForTesting
+    public IpSecConfig() {}
 
     private IpSecConfig(Parcel in) {
-        localAddress = readInetAddressFromParcel(in);
-        remoteAddress = readInetAddressFromParcel(in);
-        network = (Network) in.readParcelable(Network.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_IN].spiResourceId = in.readInt();
-        flow[IpSecTransform.DIRECTION_IN].encryption =
+        mMode = in.readInt();
+        mLocalAddress = in.readString();
+        mRemoteAddress = in.readString();
+        mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
+        mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
+        mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_IN].authentication =
+        mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_OUT].spiResourceId = in.readInt();
-        flow[IpSecTransform.DIRECTION_OUT].encryption =
+        mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
+        mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_OUT].authentication =
+        mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        encapType = in.readInt();
-        encapLocalPortResourceId = in.readInt();
-        encapRemotePort = in.readInt();
+        mEncapType = in.readInt();
+        mEncapSocketResourceId = in.readInt();
+        mEncapRemotePort = in.readInt();
+        mNattKeepaliveInterval = in.readInt();
     }
 
     @Override
     public String toString() {
         StringBuilder strBuilder = new StringBuilder();
         strBuilder
-                .append("{mode=")
-                .append(mode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
-                .append(", localAddress=")
-                .append(localAddress)
-                .append(", remoteAddress=")
-                .append(remoteAddress)
-                .append(", network=")
-                .append(network)
-                .append(", encapType=")
-                .append(encapType)
-                .append(", encapLocalPortResourceId=")
-                .append(encapLocalPortResourceId)
-                .append(", encapRemotePort=")
-                .append(encapRemotePort)
-                .append(", nattKeepaliveInterval=")
-                .append(nattKeepaliveInterval)
-                .append(", flow[OUT]=")
-                .append(flow[IpSecTransform.DIRECTION_OUT])
-                .append(", flow[IN]=")
-                .append(flow[IpSecTransform.DIRECTION_IN])
+                .append("{mMode=")
+                .append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
+                .append(", mLocalAddress=")
+                .append(mLocalAddress)
+                .append(", mRemoteAddress=")
+                .append(mRemoteAddress)
+                .append(", mNetwork=")
+                .append(mNetwork)
+                .append(", mEncapType=")
+                .append(mEncapType)
+                .append(", mEncapSocketResourceId=")
+                .append(mEncapSocketResourceId)
+                .append(", mEncapRemotePort=")
+                .append(mEncapRemotePort)
+                .append(", mNattKeepaliveInterval=")
+                .append(mNattKeepaliveInterval)
+                .append(", mFlow[OUT]=")
+                .append(mFlow[IpSecTransform.DIRECTION_OUT])
+                .append(", mFlow[IN]=")
+                .append(mFlow[IpSecTransform.DIRECTION_IN])
                 .append("}");
 
         return strBuilder.toString();
@@ -218,4 +271,23 @@
                     return new IpSecConfig[size];
                 }
             };
+
+    @VisibleForTesting
+    /** Equals method used for testing */
+    public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
+        if (lhs == null || rhs == null) return (lhs == rhs);
+        return (lhs.mMode == rhs.mMode
+                && lhs.mLocalAddress.equals(rhs.mLocalAddress)
+                && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+                && ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
+                        || (lhs.mNetwork == rhs.mNetwork))
+                && lhs.mEncapType == rhs.mEncapType
+                && lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
+                && lhs.mEncapRemotePort == rhs.mEncapRemotePort
+                && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
+                && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
+                        rhs.mFlow[IpSecTransform.DIRECTION_OUT])
+                && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
+                        rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+    }
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d7908c8..d7b3256 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -26,6 +26,8 @@
 import android.util.AndroidException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import dalvik.system.CloseGuard;
 
 import java.io.FileDescriptor;
@@ -188,7 +190,8 @@
         }
 
         /** @hide */
-        int getResourceId() {
+        @VisibleForTesting
+        public int getResourceId() {
             return mResourceId;
         }
     }
@@ -489,7 +492,8 @@
         }
 
         /** @hide */
-        int getResourceId() {
+        @VisibleForTesting
+        public int getResourceId() {
             return mResourceId;
         }
     };
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 62fd65b..e15a2c6 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -68,10 +68,10 @@
     public @interface TransformDirection {}
 
     /** @hide */
-    public static final int MODE_TUNNEL = 0;
+    public static final int MODE_TRANSPORT = 0;
 
     /** @hide */
-    public static final int MODE_TRANSPORT = 1;
+    public static final int MODE_TUNNEL = 1;
 
     /** @hide */
     public static final int ENCAP_NONE = 0;
@@ -113,7 +113,11 @@
         return IIpSecService.Stub.asInterface(b);
     }
 
-    private void checkResultStatusAndThrow(int status)
+    /**
+     * Checks the result status and throws an appropriate exception if
+     * the status is not Status.OK.
+     */
+    private void checkResultStatus(int status)
             throws IOException, IpSecManager.ResourceUnavailableException,
                     IpSecManager.SpiUnavailableException {
         switch (status) {
@@ -141,7 +145,7 @@
                 IpSecTransformResponse result =
                         svc.createTransportModeTransform(mConfig, new Binder());
                 int status = result.status;
-                checkResultStatusAndThrow(status);
+                checkResultStatus(status);
                 mResourceId = result.resourceId;
 
                 /* Keepalive will silently fail if not needed by the config; but, if needed and
@@ -243,61 +247,20 @@
 
     /* Package */
     void startKeepalive(Context c) {
-        // FIXME: NO_KEEPALIVE needs to be a constant
-        if (mConfig.getNattKeepaliveInterval() == 0) {
-            return;
-        }
-
-        ConnectivityManager cm =
-                (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
-
-        if (mKeepalive != null) {
-            Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
-            return;
-        }
-
-        synchronized (mKeepaliveSyncLock) {
-            mKeepalive =
-                    cm.startNattKeepalive(
-                            mConfig.getNetwork(),
-                            mConfig.getNattKeepaliveInterval(),
-                            mKeepaliveCallback,
-                            mConfig.getLocalAddress(),
-                            0x1234, /* FIXME: get the real port number again,
-                                    which we need to retrieve from the provided
-                                    EncapsulationSocket, and which isn't currently
-                                    stashed in IpSecConfig */
-                            mConfig.getRemoteAddress());
-            try {
-                // FIXME: this is still a horrible way to fudge the synchronous callback
-                mKeepaliveSyncLock.wait(2000);
-            } catch (InterruptedException e) {
-            }
-        }
-        if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
-            throw new UnsupportedOperationException("Packet Keepalive cannot be started");
+        if (mConfig.getNattKeepaliveInterval() != 0) {
+            Log.wtf(TAG, "Keepalive not yet supported.");
         }
     }
 
-    /* Package */
-    int getResourceId() {
+    /** @hide */
+    @VisibleForTesting
+    public int getResourceId() {
         return mResourceId;
     }
 
     /* Package */
     void stopKeepalive() {
-        if (mKeepalive == null) {
-            return;
-        }
-        mKeepalive.stop();
-        synchronized (mKeepaliveSyncLock) {
-            if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
-                try {
-                    mKeepaliveSyncLock.wait(2000);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
+        return;
     }
 
     /**
@@ -323,7 +286,7 @@
          */
         public IpSecTransform.Builder setEncryption(
                 @TransformDirection int direction, IpSecAlgorithm algo) {
-            mConfig.flow[direction].encryption = algo;
+            mConfig.setEncryption(direction, algo);
             return this;
         }
 
@@ -338,7 +301,7 @@
          */
         public IpSecTransform.Builder setAuthentication(
                 @TransformDirection int direction, IpSecAlgorithm algo) {
-            mConfig.flow[direction].authentication = algo;
+            mConfig.setAuthentication(direction, algo);
             return this;
         }
 
@@ -361,9 +324,7 @@
          */
         public IpSecTransform.Builder setSpi(
                 @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
-            // TODO: convert to using the resource Id of the SPI. Then build() can validate
-            // the owner in the IpSecService
-            mConfig.flow[direction].spiResourceId = spi.getResourceId();
+            mConfig.setSpiResourceId(direction, spi.getResourceId());
             return this;
         }
 
@@ -378,7 +339,7 @@
          */
         @SystemApi
         public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
-            mConfig.network = net;
+            mConfig.setNetwork(net);
             return this;
         }
 
@@ -395,10 +356,9 @@
          */
         public IpSecTransform.Builder setIpv4Encapsulation(
                 IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
-            // TODO: check encap type is valid.
-            mConfig.encapType = ENCAP_ESPINUDP;
-            mConfig.encapLocalPortResourceId = localSocket.getResourceId();
-            mConfig.encapRemotePort = remotePort;
+            mConfig.setEncapType(ENCAP_ESPINUDP);
+            mConfig.setEncapSocketResourceId(localSocket.getResourceId());
+            mConfig.setEncapRemotePort(remotePort);
             return this;
         }
 
@@ -416,7 +376,7 @@
          */
         @SystemApi
         public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
-            mConfig.nattKeepaliveInterval = intervalSeconds;
+            mConfig.setNattKeepaliveInterval(intervalSeconds);
             return this;
         }
 
@@ -449,10 +409,8 @@
         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;
+            mConfig.setMode(MODE_TRANSPORT);
+            mConfig.setRemoteAddress(remoteAddress.getHostAddress());
             return new IpSecTransform(mContext, mConfig).activate();
         }
 
@@ -473,9 +431,9 @@
                 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;
+            mConfig.setLocalAddress(localAddress.getHostAddress());
+            mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+            mConfig.setMode(MODE_TUNNEL);
             return new IpSecTransform(mContext, mConfig);
         }
 
@@ -489,14 +447,5 @@
             mContext = context;
             mConfig = new IpSecConfig();
         }
-
-        /**
-         * Return an {@link IpSecConfig} object for testing purposes.
-         * @hide
-         */
-        @VisibleForTesting
-        public IpSecConfig getIpSecConfig() {
-            return mConfig;
-        }
     }
 }
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 951aa8d..e01a1a6 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -391,7 +391,7 @@
         // and the length of the tag.
         // Note: we implicitly accept possible truncation for Modified-UTF8 differences. It
         //       is too expensive to compute that ahead of time.
-        int bufferSize = NoPreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD  // Base.
+        int bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD    // Base.
                 - 2                                                // Two terminators.
                 - (tag != null ? tag.length() : 0)                 // Tag length.
                 - 32;                                              // Some slack.
@@ -428,10 +428,10 @@
     }
 
     /**
-     * NoPreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
+     * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
      * a JNI call during logging.
      */
-    static class NoPreloadHolder {
+    static class PreloadHolder {
         public final static int LOGGER_ENTRY_MAX_PAYLOAD =
                 logger_entry_max_payload_native();
     }
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 18f3177..09e37e1 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -40,6 +40,7 @@
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
 
+#include "android-base/stringprintf.h"
 #include "nativebridge/native_bridge.h"
 #include "nativeloader/native_loader.h"
 
@@ -265,6 +266,8 @@
 
 // ------------------------------------------------------------------------
 
+static thread_local std::string g_error_msg;
+
 static jlong
 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
         jobject messageQueue, jstring internalDataDir, jstring obbDir,
@@ -277,7 +280,6 @@
     ScopedUtfChars pathStr(env, path);
     std::unique_ptr<NativeCode> code;
     bool needs_native_bridge = false;
-    std::string error_msg;
 
     void* handle = OpenNativeLibrary(env,
                                      sdkVersion,
@@ -285,12 +287,12 @@
                                      classLoader,
                                      libraryPath,
                                      &needs_native_bridge,
-                                     &error_msg);
+                                     &g_error_msg);
 
     if (handle == nullptr) {
         ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
               pathStr.c_str(),
-              error_msg.c_str());
+              g_error_msg.c_str());
         return 0;
     }
 
@@ -306,19 +308,22 @@
     env->ReleaseStringUTFChars(funcName, funcStr);
 
     if (code->createActivityFunc == NULL) {
-        ALOGW("ANativeActivity_onCreate not found");
+        g_error_msg = needs_native_bridge ? NativeBridgeGetError() : dlerror();
+        ALOGW("ANativeActivity_onCreate not found: %s", g_error_msg.c_str());
         return 0;
     }
 
     code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
     if (code->messageQueue == NULL) {
-        ALOGW("Unable to retrieve native MessageQueue");
+        g_error_msg = "Unable to retrieve native MessageQueue";
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
 
     int msgpipe[2];
     if (pipe(msgpipe)) {
-        ALOGW("could not create pipe: %s", strerror(errno));
+        g_error_msg = android::base::StringPrintf("could not create pipe: %s", strerror(errno));
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
     code->mainWorkRead = msgpipe[0];
@@ -334,7 +339,8 @@
 
     code->ANativeActivity::callbacks = &code->callbacks;
     if (env->GetJavaVM(&code->vm) < 0) {
-        ALOGW("NativeActivity GetJavaVM failed");
+        g_error_msg = "NativeActivity GetJavaVM failed";
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
     code->env = env;
@@ -381,7 +387,9 @@
 }
 
 static jstring getDlError_native(JNIEnv* env, jobject clazz) {
-  return env->NewStringUTF(dlerror());
+  jstring result = env->NewStringUTF(g_error_msg.c_str());
+  g_error_msg.clear();
+  return result;
 }
 
 static void
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 315eac1..9379ea6 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,28 +29,33 @@
 using vintf::RuntimeInfo;
 using vintf::VintfObject;
 
-#define MAP_STRING_METHOD(javaMethod, cppString)                                       \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags)                                \
     static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
     {                                                                                  \
-        std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo();       \
+        std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(         \
+                false /* skipCache */, flags);                                         \
         if (info == nullptr) return nullptr;                                           \
         return env->NewStringUTF((cppString).c_str());                                 \
     }                                                                                  \
 
-MAP_STRING_METHOD(getCpuInfo, info->cpuInfo());
-MAP_STRING_METHOD(getOsName, info->osName());
-MAP_STRING_METHOD(getNodeName, info->nodeName());
-MAP_STRING_METHOD(getOsRelease, info->osRelease());
-MAP_STRING_METHOD(getOsVersion, info->osVersion());
-MAP_STRING_METHOD(getHardwareId, info->hardwareId());
-MAP_STRING_METHOD(getKernelVersion, vintf::to_string(info->kernelVersion()));
-MAP_STRING_METHOD(getBootAvbVersion, vintf::to_string(info->bootAvbVersion()));
-MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbVersion()));
+MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
+MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getNodeName, info->nodeName(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getOsRelease, info->osRelease(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getOsVersion, info->osVersion(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getHardwareId, info->hardwareId(), RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getKernelVersion, vintf::to_string(info->kernelVersion()),
+                  RuntimeInfo::FetchFlag::CPU_VERSION);
+MAP_STRING_METHOD(getBootAvbVersion, vintf::to_string(info->bootAvbVersion()),
+                  RuntimeInfo::FetchFlag::AVB);
+MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbVersion()),
+                  RuntimeInfo::FetchFlag::AVB);
 
 
 static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
 {
-    std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo();
+    std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
+        false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
     if (info == nullptr) return 0;
     return static_cast<jlong>(info->kernelSepolicyVersion());
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0668b8b..29f67a8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -203,8 +203,8 @@
         android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
     <protected-broadcast
         android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
-    <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
     <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index 2784193..c5e112b 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -33,29 +33,17 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-    -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
 
 LOCAL_MIN_SDK_VERSION := 8
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
 
 ## The application with a full main dex
 include $(CLEAR_VARS)
@@ -76,26 +64,14 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=multidex -D jack.preprocessor=true\
-    -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
 
 LOCAL_MIN_SDK_VERSION := 8
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList2)
-endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp
deleted file mode 100644
index a1f5656..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
-  @@com.android.jack.annotations.ForceInMainDex
-  class com.android.multidexlegacytestapp.Test
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 1c7d807..da48df9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -32,24 +32,12 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-    -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
-  @@com.android.jack.annotations.ForceInMainDex
-  class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index b77cf31..02b3f53 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -32,24 +32,12 @@
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-    -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
-  @@com.android.jack.annotations.ForceInMainDex
-  class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 3631626..4808684 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -32,25 +32,13 @@
 LOCAL_DEX_PREOPT := false
 
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-    -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
-
-#################################
-include $(BUILD_SYSTEM)/configure_local_jack.mk
-#################################
-
-ifdef LOCAL_JACK_ENABLED
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
-endif
 
 include $(BUILD_PACKAGE)
 
-ifndef LOCAL_JACK_ENABLED
 $(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
-endif
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp
deleted file mode 100644
index 6d384e3..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/test.jpp
+++ /dev/null
@@ -1,3 +0,0 @@
-test:
-  @@com.android.jack.annotations.ForceInMainDex
-  class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index e764034..c4c14c9e 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -79,7 +79,7 @@
                 "libutils",
             ],
             shared_libs: [
-                "libz-host",
+                "libz",
             ],
         },
         windows: {
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index fb91bbb..efaf224 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -25,12 +25,12 @@
 
 
 /**
- * AmrInputStream
+ * DO NOT USE
  * @hide
  */
 public final class AmrInputStream extends InputStream {
     private final static String TAG = "AmrInputStream";
-    
+
     // frame is 20 msec at 8.000 khz
     private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
 
@@ -51,10 +51,10 @@
     private byte[] mOneByte = new byte[1];
 
     /**
-     * Create a new AmrInputStream, which converts 16 bit PCM to AMR
-     * @param inputStream InputStream containing 16 bit PCM.
+     * DO NOT USE - use MediaCodec instead
      */
     public AmrInputStream(InputStream inputStream) {
+        Log.w(TAG, "@@@@ AmrInputStream is not a public API @@@@");
         mInputStream = inputStream;
 
         MediaFormat format  = new MediaFormat();
@@ -83,17 +83,26 @@
         mInfo = new BufferInfo();
     }
 
+    /**
+     * DO NOT USE
+     */
     @Override
     public int read() throws IOException {
         int rtn = read(mOneByte, 0, 1);
         return rtn == 1 ? (0xff & mOneByte[0]) : -1;
     }
 
+    /**
+     * DO NOT USE
+     */
     @Override
     public int read(byte[] b) throws IOException {
         return read(b, 0, b.length);
     }
 
+    /**
+     * DO NOT USE
+     */
     @Override
     public int read(byte[] b, int offset, int length) throws IOException {
         if (mCodec == null) {
@@ -131,19 +140,15 @@
                 }
             }
 
-            // now read encoded data from the encoder (blocking, since we just filled up the
-            // encoder's input with data it should be able to output at least one buffer)
-            while (true) {
-                int index = mCodec.dequeueOutputBuffer(mInfo, -1);
-                if (index >= 0) {
-                    mBufIn = mInfo.size;
-                    ByteBuffer out = mCodec.getOutputBuffer(index);
-                    out.get(mBuf, 0 /* offset */, mBufIn /* length */);
-                    mCodec.releaseOutputBuffer(index,  false /* render */);
-                    if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        mSawOutputEOS = true;
-                    }
-                    break;
+            // now read encoded data from the encoder
+            int index = mCodec.dequeueOutputBuffer(mInfo, 0);
+            if (index >= 0) {
+                mBufIn = mInfo.size;
+                ByteBuffer out = mCodec.getOutputBuffer(index);
+                out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+                mCodec.releaseOutputBuffer(index,  false /* render */);
+                if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    mSawOutputEOS = true;
                 }
             }
         }
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 56a5737..8437d03 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -838,6 +838,33 @@
         return MtpConstants.RESPONSE_OK;
     }
 
+    private int moveObject(int handle, int newParent, String newPath) {
+        String[] whereArgs = new String[] {  Integer.toString(handle) };
+
+        // do not allow renaming any of the special subdirectories
+        if (isStorageSubDirectory(newPath)) {
+            return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+        }
+
+        // update database
+        ContentValues values = new ContentValues();
+        values.put(Files.FileColumns.DATA, newPath);
+        values.put(Files.FileColumns.PARENT, newParent);
+        int updated = 0;
+        try {
+            // note - we are relying on a special case in MediaProvider.update() to update
+            // the paths for all children in the case where this is a directory.
+            updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in mMediaProvider.update", e);
+        }
+        if (updated == 0) {
+            Log.e(TAG, "Unable to update path for " + handle + " to " + newPath);
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        }
+        return MtpConstants.RESPONSE_OK;
+    }
+
     private int setObjectProperty(int handle, int property,
                             long intValue, String stringValue) {
         switch (property) {
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index cf4458a..b9d3d8f 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -68,6 +68,7 @@
 static jmethodID method_getObjectInfo;
 static jmethodID method_getObjectFilePath;
 static jmethodID method_deleteFile;
+static jmethodID method_moveObject;
 static jmethodID method_getObjectReferences;
 static jmethodID method_setObjectReferences;
 static jmethodID method_sessionStarted;
@@ -178,6 +179,9 @@
 
     virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
 
+    virtual MtpResponseCode         moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+                                            MtpString& newPath);
+
     virtual void                    sessionStarted();
 
     virtual void                    sessionEnded();
@@ -993,6 +997,18 @@
     return result;
 }
 
+MtpResponseCode MyMtpDatabase::moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+        MtpString &newPath) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring stringValue = env->NewStringUTF((const char *) newPath);
+    MtpResponseCode result = env->CallIntMethod(mDatabase, method_moveObject,
+                (jint)handle, (jint)newParent, stringValue);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(stringValue);
+    return result;
+}
+
 struct PropertyTableEntry {
     MtpObjectProperty   property;
     int                 type;
@@ -1358,6 +1374,11 @@
         ALOGE("Can't find deleteFile");
         return -1;
     }
+    method_moveObject = env->GetMethodID(clazz, "moveObject", "(IILjava/lang/String;)I");
+    if (method_moveObject == NULL) {
+        ALOGE("Can't find moveObject");
+        return -1;
+    }
     method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
     if (method_getObjectReferences == NULL) {
         ALOGE("Can't find getObjectReferences");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 50e4f3d..05e0c9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1440,6 +1440,11 @@
             }
         };
 
+        if (hideAnimatedList.isEmpty()) {
+            animationFinishAction.run();
+            return;
+        }
+
         // let's disable our normal animations
         mStackScroller.setDismissAllInProgress(true);
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 3056831..2e1f142 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -33,6 +33,7 @@
 import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
 import android.net.IpSecUdpEncapResponse;
+import android.net.NetworkUtils;
 import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.IBinder;
@@ -42,11 +43,14 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -54,6 +58,7 @@
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import libcore.io.IoUtils;
 
 /** @hide */
@@ -252,7 +257,11 @@
             return (mReferenceCount.get() > 0);
         }
 
-        public void checkOwnerOrSystemAndThrow() {
+        /**
+         * Ensures that the caller is either the owner of this resource or has the system UID and
+         * throws a SecurityException otherwise.
+         */
+        public void checkOwnerOrSystem() {
             if (uid != Binder.getCallingUid()
                     && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
                 throw new SecurityException("Only the owner may access managed resources!");
@@ -335,12 +344,12 @@
     private class ManagedResourceArray<T extends ManagedResource> {
         SparseArray<T> mArray = new SparseArray<>();
 
-        T get(int key) {
+        T getAndCheckOwner(int key) {
             T val = mArray.get(key);
             // The value should never be null unless the resource doesn't exist
             // (since we do not allow null resources to be added).
             if (val != null) {
-                val.checkOwnerOrSystemAndThrow();
+                val.checkOwnerOrSystem();
             }
             return val;
         }
@@ -405,12 +414,8 @@
                             .ipSecDeleteSecurityAssociation(
                                     mResourceId,
                                     direction,
-                                    (mConfig.getLocalAddress() != null)
-                                            ? mConfig.getLocalAddress().getHostAddress()
-                                            : "",
-                                    (mConfig.getRemoteAddress() != null)
-                                            ? mConfig.getRemoteAddress().getHostAddress()
-                                            : "",
+                                    mConfig.getLocalAddress(),
+                                    mConfig.getRemoteAddress(),
                                     spi);
                 } catch (ServiceSpecificException e) {
                     // FIXME: get the error code and throw is at an IOException from Errno Exception
@@ -638,11 +643,45 @@
         }
     }
 
+    /**
+     * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
+     * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
+     */
+    private static void checkInetAddress(String inetAddress) {
+        if (TextUtils.isEmpty(inetAddress)) {
+            throw new IllegalArgumentException("Unspecified address");
+        }
+
+        InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress);
+
+        if (checkAddr.isAnyLocalAddress()) {
+            throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress);
+        }
+    }
+
+    /**
+     * Checks the user-provided direction field and throws an IllegalArgumentException if it is not
+     * DIRECTION_IN or DIRECTION_OUT
+     */
+    private static void checkDirection(int direction) {
+        switch (direction) {
+            case IpSecTransform.DIRECTION_OUT:
+            case IpSecTransform.DIRECTION_IN:
+                return;
+        }
+        throw new IllegalArgumentException("Invalid Direction: " + direction);
+    }
+
     @Override
     /** Get a new SPI and maintain the reservation in the system server */
     public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
             int direction, String remoteAddress, int requestedSpi, IBinder binder)
             throws RemoteException {
+        checkDirection(direction);
+        checkInetAddress(remoteAddress);
+        /* requestedSpi can be anything in the int range, so no check is needed. */
+        checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
+
         int resourceId = mNextResourceId.getAndIncrement();
 
         int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -651,9 +690,7 @@
         try {
             if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
                 return new IpSecSpiResponse(
-                        IpSecManager.Status.RESOURCE_UNAVAILABLE,
-                        INVALID_RESOURCE_ID,
-                        spi);
+                        IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
             }
             spi =
                     mSrvConfig
@@ -686,7 +723,7 @@
             throws RemoteException {
         // We want to non-destructively get so that we can check credentials before removing
         // this from the records.
-        T record = resArray.get(resourceId);
+        T record = resArray.getAndCheckOwner(resourceId);
 
         if (record == null) {
             throw new IllegalArgumentException(
@@ -751,6 +788,8 @@
             throw new IllegalArgumentException(
                     "Specified port number must be a valid non-reserved UDP port");
         }
+        checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+
         int resourceId = mNextResourceId.getAndIncrement();
         FileDescriptor sockFd = null;
         try {
@@ -792,6 +831,68 @@
     }
 
     /**
+     * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
+     * IllegalArgumentException if they are not.
+     */
+    private void checkIpSecConfig(IpSecConfig config) {
+        if (config.getLocalAddress() == null) {
+            throw new IllegalArgumentException("Invalid null Local InetAddress");
+        }
+
+        if (config.getRemoteAddress() == null) {
+            throw new IllegalArgumentException("Invalid null Remote InetAddress");
+        }
+
+        switch (config.getMode()) {
+            case IpSecTransform.MODE_TRANSPORT:
+                if (!config.getLocalAddress().isEmpty()) {
+                    throw new IllegalArgumentException("Non-empty Local Address");
+                }
+                // Must be valid, and not a wildcard
+                checkInetAddress(config.getRemoteAddress());
+                break;
+            case IpSecTransform.MODE_TUNNEL:
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid IpSecTransform.mode: " + config.getMode());
+        }
+
+        switch (config.getEncapType()) {
+            case IpSecTransform.ENCAP_NONE:
+                break;
+            case IpSecTransform.ENCAP_ESPINUDP:
+            case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
+                if (mUdpSocketRecords.getAndCheckOwner(
+                            config.getEncapSocketResourceId()) == null) {
+                    throw new IllegalStateException(
+                            "No Encapsulation socket for Resource Id: "
+                                    + config.getEncapSocketResourceId());
+                }
+
+                int port = config.getEncapRemotePort();
+                if (port <= 0 || port > 0xFFFF) {
+                    throw new IllegalArgumentException("Invalid remote UDP port: " + port);
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
+        }
+
+        for (int direction : DIRECTIONS) {
+            IpSecAlgorithm crypt = config.getEncryption(direction);
+            IpSecAlgorithm auth = config.getAuthentication(direction);
+            if (crypt == null && auth == null) {
+                throw new IllegalArgumentException("Encryption and Authentication are both null");
+            }
+
+            if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
+                throw new IllegalStateException("No SPI for specified Resource Id");
+            }
+        }
+    }
+
+    /**
      * Create a transport mode transform, which represent two security associations (one in each
      * direction) in the kernel. The transform will be cached by the system server and must be freed
      * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
@@ -801,17 +902,19 @@
     @Override
     public synchronized IpSecTransformResponse createTransportModeTransform(
             IpSecConfig c, IBinder binder) throws RemoteException {
+        checkIpSecConfig(c);
+        checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
         int resourceId = mNextResourceId.getAndIncrement();
         if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
         }
         SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
-        // TODO: Basic input validation here since it's coming over the Binder
+
         int encapType, encapLocalPort = 0, encapRemotePort = 0;
         UdpSocketRecord socketRecord = null;
         encapType = c.getEncapType();
         if (encapType != IpSecTransform.ENCAP_NONE) {
-            socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId());
+            socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
             encapLocalPort = socketRecord.getPort();
             encapRemotePort = c.getEncapRemotePort();
         }
@@ -820,23 +923,18 @@
             IpSecAlgorithm auth = c.getAuthentication(direction);
             IpSecAlgorithm crypt = c.getEncryption(direction);
 
-            spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction));
+            spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
             int spi = spis[direction].getSpi();
             try {
-                mSrvConfig.getNetdInstance()
+                mSrvConfig
+                        .getNetdInstance()
                         .ipSecAddSecurityAssociation(
                                 resourceId,
                                 c.getMode(),
                                 direction,
-                                (c.getLocalAddress() != null)
-                                        ? c.getLocalAddress().getHostAddress()
-                                        : "",
-                                (c.getRemoteAddress() != null)
-                                        ? c.getRemoteAddress().getHostAddress()
-                                        : "",
-                                (c.getNetwork() != null)
-                                        ? c.getNetwork().getNetworkHandle()
-                                        : 0,
+                                c.getLocalAddress(),
+                                c.getRemoteAddress(),
+                                (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
                                 spi,
                                 (auth != null) ? auth.getName() : "",
                                 (auth != null) ? auth.getKey() : null,
@@ -879,7 +977,7 @@
         // Synchronize liberally here because we are using ManagedResources in this block
         TransformRecord info;
         // FIXME: this code should be factored out into a security check + getter
-        info = mTransformRecords.get(resourceId);
+        info = mTransformRecords.getAndCheckOwner(resourceId);
 
         if (info == null) {
             throw new IllegalArgumentException("Transform " + resourceId + " is not active");
@@ -899,12 +997,8 @@
                                 socket.getFileDescriptor(),
                                 resourceId,
                                 direction,
-                                (c.getLocalAddress() != null)
-                                        ? c.getLocalAddress().getHostAddress()
-                                        : "",
-                                (c.getRemoteAddress() != null)
-                                        ? c.getRemoteAddress().getHostAddress()
-                                        : "",
+                                c.getLocalAddress(),
+                                c.getRemoteAddress(),
                                 info.getSpiRecord(direction).getSpi());
             }
         } catch (ServiceSpecificException e) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f0528bc..355e949 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1980,8 +1980,12 @@
 
         final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
         final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
+        final boolean useTls = false;
+        final String tlsHostname = "";
+        final String[] tlsFingerprints = new String[0];
         try {
-            mNetdService.setResolverConfiguration(netId, servers, domainStrs, params);
+            mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
+                    useTls, tlsHostname, tlsFingerprints);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b8fe884..108cd325 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -500,11 +500,12 @@
             if (DEBUG) {
                 Slog.d(TAG, "Receieved: " + action);
             }
+            final String pkgName = getPackageName(intent);
+            final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+
             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
                 // Purge the app's jobs if the whole package was just disabled.  When this is
                 // the case the component name will be a bare package name.
-                final String pkgName = getPackageName(intent);
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                 if (pkgName != null && pkgUid != -1) {
                     final String[] changedComponents = intent.getStringArrayExtra(
                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
@@ -524,7 +525,8 @@
                                             Slog.d(TAG, "Removing jobs for package " + pkgName
                                                     + " in user " + userId);
                                         }
-                                        cancelJobsForUid(pkgUid, "app package state changed");
+                                        cancelJobsForPackageAndUid(pkgName, pkgUid,
+                                                "app disabled");
                                     }
                                 } catch (RemoteException|IllegalArgumentException e) {
                                     /*
@@ -553,7 +555,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
                     }
-                    cancelJobsForUid(uidRemoved, "app uninstalled");
+                    cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
                 }
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -564,8 +566,6 @@
             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                 // Has this package scheduled any jobs, such that we will take action
                 // if it were to be force-stopped?
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                final String pkgName = intent.getData().getSchemeSpecificPart();
                 if (pkgUid != -1) {
                     List<JobStatus> jobsForUid;
                     synchronized (mLock) {
@@ -584,13 +584,11 @@
                 }
             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
                 // possible force-stop
-                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                final String pkgName = intent.getData().getSchemeSpecificPart();
                 if (pkgUid != -1) {
                     if (DEBUG) {
                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
                     }
-                    cancelJobsForPackageAndUid(pkgName, pkgUid);
+                    cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
                 }
             }
         }
@@ -759,13 +757,17 @@
         }
     }
 
-    void cancelJobsForPackageAndUid(String pkgName, int uid) {
+    void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
+        if ("android".equals(pkgName)) {
+            Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
+            return;
+        }
         synchronized (mLock) {
             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
                 final JobStatus job = jobsForUid.get(i);
                 if (job.getSourcePackageName().equals(pkgName)) {
-                    cancelJobImplLocked(job, null, "app force stopped");
+                    cancelJobImplLocked(job, null, reason);
                 }
             }
         }
@@ -779,6 +781,10 @@
      *
      */
     public void cancelJobsForUid(int uid, String reason) {
+        if (uid == Process.SYSTEM_UID) {
+            Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
+            return;
+        }
         synchronized (mLock) {
             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
             for (int i=0; i<jobsForUid.size(); i++) {
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 884a8a7..0230f36 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -61,11 +61,11 @@
     private static final String MARK_STOP = "--- STOP ---";
 
     private final String mTag;
-    private final Handler mHandler;
     private final LocalLog mLog;
     private final BlockingSocketReader mPacketListener;
+    private boolean mRunning;
 
-    public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) {
+    public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
         final String ifname;
         final int ifindex;
         final byte[] hwaddr;
@@ -81,44 +81,40 @@
         }
 
         mTag = TAG + "." + ifname;
-        mHandler = new Handler();
         mLog = log;
-        mPacketListener = new PacketListener(ifindex, hwaddr, mtu);
+        mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
     }
 
     public void start() {
-        mLog.log(MARK_START);
+        mRunning = true;
         mPacketListener.start();
     }
 
     public void stop() {
         mPacketListener.stop();
-        mLog.log(MARK_STOP);
+        mRunning = false;
     }
 
     private final class PacketListener extends BlockingSocketReader {
         private final int mIfIndex;
         private final byte mHwAddr[];
 
-        PacketListener(int ifindex, byte[] hwaddr, int mtu) {
-            super(mtu);
+        PacketListener(Handler h, int ifindex, byte[] hwaddr, int mtu) {
+            super(h, mtu);
             mIfIndex = ifindex;
             mHwAddr = hwaddr;
         }
 
         @Override
-        protected FileDescriptor createSocket() {
+        protected FileDescriptor createFd() {
             FileDescriptor s = null;
             try {
-                // TODO: Evaluate switching to SOCK_DGRAM and changing the
-                // BlockingSocketReader's read() to recvfrom(), so that this
-                // might work on non-ethernet-like links (via SLL).
                 s = Os.socket(AF_PACKET, SOCK_RAW, 0);
                 NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
                 Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
             } catch (ErrnoException | IOException e) {
                 logError("Failed to create packet tracking socket: ", e);
-                closeSocket(s);
+                closeFd(s);
                 return null;
             }
             return s;
@@ -136,13 +132,27 @@
         }
 
         @Override
+        protected void onStart() {
+            mLog.log(MARK_START);
+        }
+
+        @Override
+        protected void onStop() {
+            if (mRunning) {
+                mLog.log(MARK_STOP);
+            } else {
+                mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)");
+            }
+        }
+
+        @Override
         protected void logError(String msg, Exception e) {
             Log.e(mTag, msg, e);
             addLogEntry(msg + e);
         }
 
         private void addLogEntry(String entry) {
-            mHandler.post(() -> mLog.log(entry));
+            mLog.log(entry);
         }
     }
 }
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index b1eb085..bc07b81 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -1515,7 +1515,8 @@
 
         private ConnectivityPacketTracker createPacketTracker() {
             try {
-                return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
+                return new ConnectivityPacketTracker(
+                        getHandler(), mNetworkInterface, mConnectivityPacketLog);
             } catch (IllegalArgumentException e) {
                 return null;
             }
diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/BlockingSocketReader.java
index 12fa1e5..99bf469 100644
--- a/services/net/java/android/net/util/BlockingSocketReader.java
+++ b/services/net/java/android/net/util/BlockingSocketReader.java
@@ -16,81 +16,106 @@
 
 package android.net.util;
 
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+
 import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
 
-import libcore.io.IoBridge;
+import libcore.io.IoUtils;
 
 import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
 import java.io.IOException;
 
 
 /**
- * A thread that reads from a socket and passes the received packets to a
- * subclass's handlePacket() method.  The packet receive buffer is recycled
- * on every read call, so subclasses should make any copies they would like
- * inside their handlePacket() implementation.
+ * This class encapsulates the mechanics of registering a file descriptor
+ * with a thread's Looper and handling read events (and errors).
  *
- * All public methods may be called from any thread.
+ * Subclasses MUST implement createFd() and SHOULD override handlePacket().
+
+ * Subclasses can expect a call life-cycle like the following:
+ *
+ *     [1] start() calls createFd() and (if all goes well) onStart()
+ *
+ *     [2] yield, waiting for read event or error notification:
+ *
+ *             [a] readPacket() && handlePacket()
+ *
+ *             [b] if (no error):
+ *                     goto 2
+ *                 else:
+ *                     goto 3
+ *
+ *     [3] stop() calls onStop() if not previously stopped
+ *
+ * The packet receive buffer is recycled on every read call, so subclasses
+ * should make any copies they would like inside their handlePacket()
+ * implementation.
+ *
+ * All public methods MUST only be called from the same thread with which
+ * the Handler constructor argument is associated.
+ *
+ * TODO: rename this class to something more correctly descriptive (something
+ * like [or less horrible than] FdReadEventsHandler?).
  *
  * @hide
  */
 public abstract class BlockingSocketReader {
+    private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+    private static final int UNREGISTER_THIS_FD = 0;
+
     public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
 
+    private final Handler mHandler;
+    private final MessageQueue mQueue;
     private final byte[] mPacket;
-    private final Thread mThread;
-    private volatile FileDescriptor mSocket;
-    private volatile boolean mRunning;
-    private volatile long mPacketsReceived;
+    private FileDescriptor mFd;
+    private long mPacketsReceived;
 
-    // Make it slightly easier for subclasses to properly close a socket
-    // without having to know this incantation.
-    public static final void closeSocket(@Nullable FileDescriptor fd) {
-        try {
-            IoBridge.closeAndSignalBlockedThreads(fd);
-        } catch (IOException ignored) {}
+    protected static void closeFd(FileDescriptor fd) {
+        IoUtils.closeQuietly(fd);
     }
 
-    protected BlockingSocketReader() {
-        this(DEFAULT_RECV_BUF_SIZE);
+    protected BlockingSocketReader(Handler h) {
+        this(h, DEFAULT_RECV_BUF_SIZE);
     }
 
-    protected BlockingSocketReader(int recvbufsize) {
-        if (recvbufsize < DEFAULT_RECV_BUF_SIZE) {
-            recvbufsize = DEFAULT_RECV_BUF_SIZE;
+    protected BlockingSocketReader(Handler h, int recvbufsize) {
+        mHandler = h;
+        mQueue = mHandler.getLooper().getQueue();
+        mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
+    }
+
+    public final void start() {
+        if (onCorrectThread()) {
+            createAndRegisterFd();
+        } else {
+            mHandler.post(() -> {
+                logError("start() called from off-thread", null);
+                createAndRegisterFd();
+            });
         }
-        mPacket = new byte[recvbufsize];
-        mThread = new Thread(() -> { mainLoop(); });
-    }
-
-    public final boolean start() {
-        if (mSocket != null) return false;
-
-        try {
-            mSocket = createSocket();
-        } catch (Exception e) {
-            logError("Failed to create socket: ", e);
-            return false;
-        }
-
-        if (mSocket == null) return false;
-
-        mRunning = true;
-        mThread.start();
-        return true;
     }
 
     public final void stop() {
-        mRunning = false;
-        closeSocket(mSocket);
-        mSocket = null;
+        if (onCorrectThread()) {
+            unregisterAndDestroyFd();
+        } else {
+            mHandler.post(() -> {
+                logError("stop() called from off-thread", null);
+                unregisterAndDestroyFd();
+            });
+        }
     }
 
-    public final boolean isRunning() { return mRunning; }
+    public final int recvBufSize() { return mPacket.length; }
 
     public final long numPacketsReceived() { return mPacketsReceived; }
 
@@ -98,11 +123,21 @@
      * Subclasses MUST create the listening socket here, including setting
      * all desired socket options, interface or address/port binding, etc.
      */
-    protected abstract FileDescriptor createSocket();
+    protected abstract FileDescriptor createFd();
+
+    /**
+     * Subclasses MAY override this to change the default read() implementation
+     * in favour of, say, recvfrom().
+     *
+     * Implementations MUST return the bytes read or throw an Exception.
+     */
+    protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
+        return Os.read(fd, packetBuffer, 0, packetBuffer.length);
+    }
 
     /**
      * Called by the main loop for every packet.  Any desired copies of
-     * |recvbuf| should be made in here, and the underlying byte array is
+     * |recvbuf| should be made in here, as the underlying byte array is
      * reused across all reads.
      */
     protected void handlePacket(byte[] recvbuf, int length) {}
@@ -113,43 +148,102 @@
     protected void logError(String msg, Exception e) {}
 
     /**
-     * Called by the main loop just prior to exiting.
+     * Called by start(), if successful, just prior to returning.
      */
-    protected void onExit() {}
+    protected void onStart() {}
 
-    private final void mainLoop() {
+    /**
+     * Called by stop() just prior to returning.
+     */
+    protected void onStop() {}
+
+    private void createAndRegisterFd() {
+        if (mFd != null) return;
+
+        try {
+            mFd = createFd();
+            if (mFd != null) {
+                // Force the socket to be non-blocking.
+                IoUtils.setBlocking(mFd, false);
+            }
+        } catch (Exception e) {
+            logError("Failed to create socket: ", e);
+            closeFd(mFd);
+            mFd = null;
+            return;
+        }
+
+        if (mFd == null) return;
+
+        mQueue.addOnFileDescriptorEventListener(
+                mFd,
+                FD_EVENTS,
+                new OnFileDescriptorEventListener() {
+                    @Override
+                    public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                        // Always call handleInput() so read/recvfrom are given
+                        // a proper chance to encounter a meaningful errno and
+                        // perhaps log a useful error message.
+                        if (!isRunning() || !handleInput()) {
+                            unregisterAndDestroyFd();
+                            return UNREGISTER_THIS_FD;
+                        }
+                        return FD_EVENTS;
+                    }
+                });
+        onStart();
+    }
+
+    private boolean isRunning() { return (mFd != null) && mFd.valid(); }
+
+    // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
+    private boolean handleInput() {
         while (isRunning()) {
             final int bytesRead;
 
             try {
-                // Blocking read.
-                // TODO: See if this can be converted to recvfrom.
-                bytesRead = Os.read(mSocket, mPacket, 0, mPacket.length);
+                bytesRead = readPacket(mFd, mPacket);
                 if (bytesRead < 1) {
                     if (isRunning()) logError("Socket closed, exiting", null);
                     break;
                 }
                 mPacketsReceived++;
             } catch (ErrnoException e) {
-                if (e.errno != OsConstants.EINTR) {
-                    if (isRunning()) logError("read error: ", e);
+                if (e.errno == OsConstants.EAGAIN) {
+                    // We've read everything there is to read this time around.
+                    return true;
+                } else if (e.errno == OsConstants.EINTR) {
+                    continue;
+                } else {
+                    if (isRunning()) logError("readPacket error: ", e);
                     break;
                 }
-                continue;
-            } catch (IOException ioe) {
-                if (isRunning()) logError("read error: ", ioe);
-                continue;
+            } catch (Exception e) {
+                if (isRunning()) logError("readPacket error: ", e);
+                break;
             }
 
             try {
                 handlePacket(mPacket, bytesRead);
             } catch (Exception e) {
-                logError("Unexpected exception: ", e);
+                logError("handlePacket error: ", e);
                 break;
             }
         }
 
-        stop();
-        onExit();
+        return false;
+    }
+
+    private void unregisterAndDestroyFd() {
+        if (mFd == null) return;
+
+        mQueue.removeOnFileDescriptorEventListener(mFd);
+        closeFd(mFd);
+        mFd = null;
+        onStop();
+    }
+
+    private boolean onCorrectThread() {
+        return (mHandler.getLooper() == Looper.myLooper());
     }
 }
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index ebac041..764b7b2 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -522,8 +522,7 @@
      * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
      */
     public void registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback,
-            @NonNull Handler handler) {
+            @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -533,7 +532,8 @@
                 new InternalDownloadStateCallback(callback, handler);
 
         try {
-            int result = downloadService.registerStateCallback(request, internalCallback);
+            int result = downloadService.registerStateCallback(request, internalCallback,
+                    callback.getCallbackFilterFlags());
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 86920bd..892fbf0 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,8 +16,12 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.telephony.MbmsDownloadSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A optional listener class used by download clients to track progress. Apps should extend this
  * class and pass an instance into
@@ -29,6 +33,71 @@
 public class DownloadStateCallback {
 
     /**
+     * Bitmask flags used for filtering out callback methods. Used when constructing the
+     * DownloadStateCallback as an optional parameter.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+    public @interface FilterFlag {}
+
+    /**
+     * Receive all callbacks.
+     * Default value.
+     */
+    public static final int ALL_UPDATES = 0x00;
+    /**
+     * Receive callbacks for {@link #onProgressUpdated}.
+     */
+    public static final int PROGRESS_UPDATES = 0x01;
+    /**
+     * Receive callbacks for {@link #onStateUpdated}.
+     */
+    public static final int STATE_UPDATES = 0x02;
+
+    private final int mCallbackFilterFlags;
+
+    /**
+     * Creates a DownloadStateCallback that will receive all callbacks.
+     */
+    public DownloadStateCallback() {
+        mCallbackFilterFlags = ALL_UPDATES;
+    }
+
+    /**
+     * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
+     * via the filterFlags parameter.
+     * @param filterFlags A bitmask of filter flags that will specify which callback this instance
+     *     is interested in.
+     */
+    public DownloadStateCallback(int filterFlags) {
+        mCallbackFilterFlags = filterFlags;
+    }
+
+    /**
+     * Return the currently set filter flags.
+     * @return An integer containing the bitmask of flags that this instance is interested in.
+     * @hide
+     */
+    public int getCallbackFilterFlags() {
+        return mCallbackFilterFlags;
+    }
+
+    /**
+     * Returns true if a filter flag is set for a particular callback method. If the flag is set,
+     * the callback will be delivered to the listening process.
+     * @param flag A filter flag specifying whether or not a callback method is registered to
+     *     receive callbacks.
+     * @return true if registered to receive callbacks in the listening process, false if not.
+     */
+    public final boolean isFilterFlagSet(@FilterFlag int flag) {
+        if (mCallbackFilterFlags == ALL_UPDATES) {
+            return true;
+        }
+        return (mCallbackFilterFlags & flag) > 0;
+    }
+
+    /**
      * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
      *
      * @param request a {@link DownloadRequest}, indicating which download is being referenced.
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 61415b5..fe27537 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,6 +165,12 @@
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
+            // We do not need to verify below extras if the result is not success.
+            if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
+                    intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+                    MbmsDownloadSession.RESULT_CANCELLED)) {
+                return true;
+            }
             if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
                 Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
                 return false;
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index 9a01ed0..8529f52 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -23,6 +23,7 @@
 import android.text.TextUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -62,12 +63,6 @@
             throw new RuntimeException("bad locales length " + newLocales.size());
         }
 
-        for (Locale l : newLocales) {
-            if (!newNames.containsKey(l)) {
-                throw new IllegalArgumentException("A name must be provided for each locale");
-            }
-        }
-
         names = new HashMap(newNames.size());
         names.putAll(newNames);
         className = newClassName;
@@ -127,7 +122,7 @@
      * Get the user-displayable name for this cell-broadcast service corresponding to the
      * provided {@link Locale}.
      * @param locale The {@link Locale} in which you want the name of the service. This must be a
-     *               value from the list returned by {@link #getLocales()} -- an
+     *               value from the set returned by {@link #getNamedContentLocales()} -- an
      *               {@link java.util.NoSuchElementException} may be thrown otherwise.
      * @return The {@link CharSequence} providing the name of the service in the given
      *         {@link Locale}
@@ -140,6 +135,17 @@
     }
 
     /**
+     * Return an unmodifiable set of the current {@link Locale}s that have a user-displayable name
+     * associated with them. The user-displayable name associated with any {@link Locale} in this
+     * set can be retrieved with {@link #getNameForLocale(Locale)}.
+     * @return An unmodifiable set of {@link Locale} objects corresponding to a user-displayable
+     * content name in that locale.
+     */
+    public @NonNull Set<Locale> getNamedContentLocales() {
+        return Collections.unmodifiableSet(names.keySet());
+    }
+
+    /**
      * The class name for this service - used to categorize and filter
      */
     public String getServiceClassName() {
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index ed5e826..cb93542 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -36,7 +36,8 @@
 
     int download(in DownloadRequest downloadRequest);
 
-    int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+    int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener,
+        int flags);
 
     int unregisterStateCallback(in DownloadRequest downloadRequest,
         IDownloadStateCallback listener);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index d845a57..2f85a1d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -46,6 +46,47 @@
     private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
     private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
 
+
+    // Filters the DownloadStateCallbacks by its configuration from the app.
+    private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+
+        private final IDownloadStateCallback mCallback;
+        public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
+            super(callbackFlags);
+            mCallback = callback;
+        }
+
+        @Override
+        public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+                int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
+                int fullDecodedSize) {
+            if (!isFilterFlagSet(PROGRESS_UPDATES)) {
+                return;
+            }
+            try {
+                mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
+
+        @Override
+        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+                @MbmsDownloadSession.DownloadStatus int state) {
+            if (!isFilterFlagSet(STATE_UPDATES)) {
+                return;
+            }
+            try {
+                mCallback.onStateUpdated(request, fileInfo, state);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
+
+        protected abstract void onRemoteException(RemoteException e);
+    }
+
     /**
      * Initialize the download service for this app and subId, registering the listener.
      *
@@ -196,9 +237,8 @@
      * @hide
      */
     @Override
-    public final int registerStateCallback(
-            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
-            throws RemoteException {
+    public final int registerStateCallback(final DownloadRequest downloadRequest,
+            final IDownloadStateCallback callback, int flags) throws RemoteException {
         final int uid = Binder.getCallingUid();
         DeathRecipient deathRecipient = new DeathRecipient() {
             @Override
@@ -211,28 +251,10 @@
         mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
         callback.asBinder().linkToDeath(deathRecipient, 0);
 
-        DownloadStateCallback exposedCallback = new DownloadStateCallback() {
+        DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
             @Override
-            public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
-                    currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
-                    fullDecodedSize) {
-                try {
-                    callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
-                            fullDownloadSize,
-                            currentDecodedSize, fullDecodedSize);
-                } catch (RemoteException e) {
-                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                }
-            }
-
-            @Override
-            public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                    @MbmsDownloadSession.DownloadStatus int state) {
-                try {
-                    callback.onStateUpdated(request, fileInfo, state);
-                } catch (RemoteException e) {
-                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                }
+            protected void onRemoteException(RemoteException e) {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
             }
         };
 
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index 8fb27b2..a43f122 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -38,8 +38,9 @@
 
     /**
      * The MBMS middleware should send this when a download of single file has completed or
-     * failed. Mandatory extras are
+     * failed. The only mandatory extra is
      * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+     * and the following are required when the download has completed:
      * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
      * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
      * {@link #EXTRA_TEMP_LIST}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
new file mode 100644
index 0000000..1b4bef5
--- /dev/null
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecConfig}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecConfigTest {
+
+    @Test
+    public void testDefaults() throws Exception {
+        IpSecConfig c = new IpSecConfig();
+        assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
+        assertEquals("", c.getLocalAddress());
+        assertEquals("", c.getRemoteAddress());
+        assertNull(c.getNetwork());
+        assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
+        assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
+        assertEquals(0, c.getEncapRemotePort());
+        assertEquals(0, c.getNattKeepaliveInterval());
+        for (int direction :
+                new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
+            assertNull(c.getEncryption(direction));
+            assertNull(c.getAuthentication(direction));
+            assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
+        }
+    }
+
+    @Test
+    public void testParcelUnparcel() throws Exception {
+        assertParcelingIsLossless(new IpSecConfig());
+
+        IpSecConfig c = new IpSecConfig();
+        c.setMode(IpSecTransform.MODE_TUNNEL);
+        c.setLocalAddress("0.0.0.0");
+        c.setRemoteAddress("1.2.3.4");
+        c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
+        c.setEncapSocketResourceId(7);
+        c.setEncapRemotePort(22);
+        c.setNattKeepaliveInterval(42);
+        c.setEncryption(
+                IpSecTransform.DIRECTION_OUT,
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.CRYPT_AES_CBC,
+                        new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
+        c.setAuthentication(
+                IpSecTransform.DIRECTION_OUT,
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA1,
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+        c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
+        c.setEncryption(
+                IpSecTransform.DIRECTION_IN,
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.CRYPT_AES_CBC,
+                        new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
+        c.setAuthentication(
+                IpSecTransform.DIRECTION_IN,
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA1,
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
+        c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+        assertParcelingIsLossless(c);
+    }
+
+    private void assertParcelingIsLossless(IpSecConfig ci) throws Exception {
+        Parcel p = Parcel.obtain();
+        ci.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p);
+        assertTrue(IpSecConfig.equals(co, ci));
+    }
+}
diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
index e03350f..1aad453 100644
--- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java
+++ b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
@@ -16,8 +16,11 @@
 
 package android.net.util;
 
+import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE;
 import static android.system.OsConstants.*;
 
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
@@ -27,6 +30,7 @@
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.Inet6Address;
@@ -53,61 +57,83 @@
     protected FileDescriptor mLocalSocket;
     protected InetSocketAddress mLocalSockName;
     protected byte[] mLastRecvBuf;
-    protected boolean mExited;
+    protected boolean mStopped;
+    protected HandlerThread mHandlerThread;
     protected BlockingSocketReader mReceiver;
 
+    class UdpLoopbackReader extends BlockingSocketReader {
+        public UdpLoopbackReader(Handler h) {
+            super(h);
+        }
+
+        @Override
+        protected FileDescriptor createFd() {
+            FileDescriptor s = null;
+            try {
+                s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+                Os.bind(s, LOOPBACK6, 0);
+                mLocalSockName = (InetSocketAddress) Os.getsockname(s);
+                Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
+            } catch (ErrnoException|SocketException e) {
+                closeFd(s);
+                fail();
+                return null;
+            }
+
+            mLocalSocket = s;
+            return s;
+        }
+
+        @Override
+        protected void handlePacket(byte[] recvbuf, int length) {
+            mLastRecvBuf = Arrays.copyOf(recvbuf, length);
+            mLatch.countDown();
+        }
+
+        @Override
+        protected void onStart() {
+            mStopped = false;
+            mLatch.countDown();
+        }
+
+        @Override
+        protected void onStop() {
+            mStopped = true;
+            mLatch.countDown();
+        }
+    };
+
     @Override
     public void setUp() {
         resetLatch();
         mLocalSocket = null;
         mLocalSockName = null;
         mLastRecvBuf = null;
-        mExited = false;
+        mStopped = false;
 
-        mReceiver = new BlockingSocketReader() {
-            @Override
-            protected FileDescriptor createSocket() {
-                FileDescriptor s = null;
-                try {
-                    s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-                    Os.bind(s, LOOPBACK6, 0);
-                    mLocalSockName = (InetSocketAddress) Os.getsockname(s);
-                    Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
-                } catch (ErrnoException|SocketException e) {
-                    closeSocket(s);
-                    fail();
-                    return null;
-                }
-
-                mLocalSocket = s;
-                return s;
-            }
-
-            @Override
-            protected void handlePacket(byte[] recvbuf, int length) {
-                mLastRecvBuf = Arrays.copyOf(recvbuf, length);
-                mLatch.countDown();
-            }
-
-            @Override
-            protected void onExit() {
-                mExited = true;
-                mLatch.countDown();
-            }
-        };
+        mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName());
+        mHandlerThread.start();
     }
 
     @Override
-    public void tearDown() {
-        if (mReceiver != null) mReceiver.stop();
+    public void tearDown() throws Exception {
+        if (mReceiver != null) {
+            mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
+            waitForActivity();
+        }
         mReceiver = null;
+        mHandlerThread.quit();
+        mHandlerThread = null;
     }
 
     void resetLatch() { mLatch = new CountDownLatch(1); }
 
     void waitForActivity() throws Exception {
-        assertTrue(mLatch.await(500, TimeUnit.MILLISECONDS));
-        resetLatch();
+        try {
+            mLatch.await(1000, TimeUnit.MILLISECONDS);
+        } finally {
+            resetLatch();
+        }
     }
 
     void sendPacket(byte[] contents) throws Exception {
@@ -118,31 +144,54 @@
     }
 
     public void testBasicWorking() throws Exception {
-        assertTrue(mReceiver.start());
+        final Handler h = mHandlerThread.getThreadHandler();
+        mReceiver = new UdpLoopbackReader(h);
+
+        h.post(() -> { mReceiver.start(); });
+        waitForActivity();
         assertTrue(mLocalSockName != null);
         assertEquals(LOOPBACK6, mLocalSockName.getAddress());
         assertTrue(0 < mLocalSockName.getPort());
         assertTrue(mLocalSocket != null);
-        assertFalse(mExited);
+        assertFalse(mStopped);
 
         final byte[] one = "one 1".getBytes("UTF-8");
         sendPacket(one);
         waitForActivity();
         assertEquals(1, mReceiver.numPacketsReceived());
         assertTrue(Arrays.equals(one, mLastRecvBuf));
-        assertFalse(mExited);
+        assertFalse(mStopped);
 
         final byte[] two = "two 2".getBytes("UTF-8");
         sendPacket(two);
         waitForActivity();
         assertEquals(2, mReceiver.numPacketsReceived());
         assertTrue(Arrays.equals(two, mLastRecvBuf));
-        assertFalse(mExited);
+        assertFalse(mStopped);
 
         mReceiver.stop();
         waitForActivity();
         assertEquals(2, mReceiver.numPacketsReceived());
         assertTrue(Arrays.equals(two, mLastRecvBuf));
-        assertTrue(mExited);
+        assertTrue(mStopped);
+        mReceiver = null;
+    }
+
+    class NullBlockingSocketReader extends BlockingSocketReader {
+        public NullBlockingSocketReader(Handler h, int recvbufsize) {
+            super(h, recvbufsize);
+        }
+
+        @Override
+        public FileDescriptor createFd() { return null; }
+    }
+
+    public void testMinimalRecvBufSize() throws Exception {
+        final Handler h = mHandlerThread.getThreadHandler();
+
+        for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
+            final BlockingSocketReader b = new NullBlockingSocketReader(h, i);
+            assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
+        }
     }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9c498c7..335e6240 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -121,7 +121,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BooleanSupplier;
 import java.util.function.Predicate;
 
 /**
@@ -3212,68 +3211,68 @@
     }
 
     @SmallTest
-    public void testNetworkRequestMaximum() {
+    public void testNetworkCallbackMaximum() {
         final int MAX_REQUESTS = 100;
-        // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
+        final int CALLBACKS = 90;
+        final int INTENTS = 10;
+        assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
+
         NetworkRequest networkRequest = new NetworkRequest.Builder().build();
-        ArrayList<NetworkCallback> networkCallbacks = new ArrayList<NetworkCallback>();
-        try {
-            for (int i = 0; i < MAX_REQUESTS; i++) {
-                NetworkCallback networkCallback = new NetworkCallback();
-                mCm.requestNetwork(networkRequest, networkCallback);
-                networkCallbacks.add(networkCallback);
-            }
-            fail("Registering " + MAX_REQUESTS + " NetworkRequests did not throw exception");
-        } catch (TooManyRequestsException expected) {}
-        for (NetworkCallback networkCallback : networkCallbacks) {
-            mCm.unregisterNetworkCallback(networkCallback);
-        }
-        networkCallbacks.clear();
+        ArrayList<Object> registered = new ArrayList<>();
 
-        try {
-            for (int i = 0; i < MAX_REQUESTS; i++) {
-                NetworkCallback networkCallback = new NetworkCallback();
-                mCm.registerNetworkCallback(networkRequest, networkCallback);
-                networkCallbacks.add(networkCallback);
-            }
-            fail("Registering " + MAX_REQUESTS + " NetworkCallbacks did not throw exception");
-        } catch (TooManyRequestsException expected) {}
-        for (NetworkCallback networkCallback : networkCallbacks) {
-            mCm.unregisterNetworkCallback(networkCallback);
+        int j = 0;
+        while (j++ < CALLBACKS / 2) {
+            NetworkCallback cb = new NetworkCallback();
+            mCm.requestNetwork(networkRequest, cb);
+            registered.add(cb);
         }
-        networkCallbacks.clear();
+        while (j++ < CALLBACKS) {
+            NetworkCallback cb = new NetworkCallback();
+            mCm.registerNetworkCallback(networkRequest, cb);
+            registered.add(cb);
+        }
+        j = 0;
+        while (j++ < INTENTS / 2) {
+            PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent("a" + j), 0);
+            mCm.requestNetwork(networkRequest, pi);
+            registered.add(pi);
+        }
+        while (j++ < INTENTS) {
+            PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent("b" + j), 0);
+            mCm.registerNetworkCallback(networkRequest, pi);
+            registered.add(pi);
+        }
 
-        ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
+        // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
         try {
-            for (int i = 0; i < MAX_REQUESTS + 1; i++) {
-                PendingIntent pendingIntent =
-                        PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
-                mCm.requestNetwork(networkRequest, pendingIntent);
-                pendingIntents.add(pendingIntent);
-            }
-            fail("Registering " + MAX_REQUESTS +
-                    " PendingIntent NetworkRequests did not throw exception");
+            mCm.requestNetwork(networkRequest, new NetworkCallback());
+            fail("Registering " + MAX_REQUESTS + " network requests did not throw exception");
         } catch (TooManyRequestsException expected) {}
-        for (PendingIntent pendingIntent : pendingIntents) {
-            mCm.unregisterNetworkCallback(pendingIntent);
-        }
-        pendingIntents.clear();
+        try {
+            mCm.registerNetworkCallback(networkRequest, new NetworkCallback());
+            fail("Registering " + MAX_REQUESTS + " network callbacks did not throw exception");
+        } catch (TooManyRequestsException expected) {}
+        try {
+            mCm.requestNetwork(networkRequest,
+                PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0));
+            fail("Registering " + MAX_REQUESTS + " PendingIntent requests did not throw exception");
+        } catch (TooManyRequestsException expected) {}
+        try {
+            mCm.registerNetworkCallback(networkRequest,
+                PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0));
+            fail("Registering " + MAX_REQUESTS
+                    + " PendingIntent callbacks did not throw exception");
+        } catch (TooManyRequestsException expected) {}
 
-        try {
-            for (int i = 0; i < MAX_REQUESTS + 1; i++) {
-                PendingIntent pendingIntent =
-                        PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
-                mCm.registerNetworkCallback(networkRequest, pendingIntent);
-                pendingIntents.add(pendingIntent);
+        for (Object o : registered) {
+            if (o instanceof NetworkCallback) {
+                mCm.unregisterNetworkCallback((NetworkCallback)o);
             }
-            fail("Registering " + MAX_REQUESTS +
-                    " PendingIntent NetworkCallbacks did not throw exception");
-        } catch (TooManyRequestsException expected) {}
-        for (PendingIntent pendingIntent : pendingIntents) {
-            mCm.unregisterNetworkCallback(pendingIntent);
+            if (o instanceof PendingIntent) {
+                mCm.unregisterNetworkCallback((PendingIntent)o);
+            }
         }
-        pendingIntents.clear();
-        waitForIdle(5000);
+        waitForIdle();
 
         // Test that the limit is not hit when MAX_REQUESTS requests are added and removed.
         for (int i = 0; i < MAX_REQUESTS; i++) {
@@ -3281,23 +3280,23 @@
             mCm.requestNetwork(networkRequest, networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
         }
-        waitForIdle();
+
         for (int i = 0; i < MAX_REQUESTS; i++) {
             NetworkCallback networkCallback = new NetworkCallback();
             mCm.registerNetworkCallback(networkRequest, networkCallback);
             mCm.unregisterNetworkCallback(networkCallback);
         }
-        waitForIdle();
+
         for (int i = 0; i < MAX_REQUESTS; i++) {
             PendingIntent pendingIntent =
-                    PendingIntent.getBroadcast(mContext, 0, new Intent("b" + i), 0);
+                    PendingIntent.getBroadcast(mContext, 0, new Intent("e" + i), 0);
             mCm.requestNetwork(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
         }
-        waitForIdle();
+
         for (int i = 0; i < MAX_REQUESTS; i++) {
             PendingIntent pendingIntent =
-                    PendingIntent.getBroadcast(mContext, 0, new Intent("c" + i), 0);
+                    PendingIntent.getBroadcast(mContext, 0, new Intent("f" + i), 0);
             mCm.registerNetworkCallback(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
         }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
new file mode 100644
index 0000000..9057a10
--- /dev/null
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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 com.android.server;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetd;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecConfig;
+import android.net.IpSecManager;
+import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
+import android.net.IpSecTransformResponse;
+import android.net.NetworkUtils;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.support.test.filters.SmallTest;
+import android.system.OsConstants;
+
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** Unit tests for {@link IpSecService}. */
+@SmallTest
+@RunWith(Parameterized.class)
+public class IpSecServiceParameterizedTest {
+
+    private static final int DROID_SPI = 0xD1201D;
+    private static final int DROID_SPI2 = DROID_SPI + 1;
+
+    private final String mRemoteAddr;
+
+    @Parameterized.Parameters
+    public static Collection ipSecConfigs() {
+        return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+    }
+
+    private static final byte[] CRYPT_KEY = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+    };
+    private static final byte[] AUTH_KEY = {
+        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
+        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
+    };
+
+    Context mMockContext;
+    INetd mMockNetd;
+    IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+    IpSecService mIpSecService;
+
+    public IpSecServiceParameterizedTest(String remoteAddr) {
+        mRemoteAddr = remoteAddr;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mMockContext = mock(Context.class);
+        mMockNetd = mock(INetd.class);
+        mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
+        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+
+        // Injecting mock netd
+        when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+    }
+
+    @Test
+    public void testIpSecServiceReserveSpi() throws Exception {
+        when(mMockNetd.ipSecAllocateSpi(
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        eq(mRemoteAddr),
+                        eq(DROID_SPI)))
+                .thenReturn(DROID_SPI);
+
+        IpSecSpiResponse spiResp =
+                mIpSecService.reserveSecurityParameterIndex(
+                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+        assertEquals(IpSecManager.Status.OK, spiResp.status);
+        assertEquals(DROID_SPI, spiResp.spi);
+    }
+
+    @Test
+    public void testReleaseSecurityParameterIndex() throws Exception {
+        when(mMockNetd.ipSecAllocateSpi(
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        eq(mRemoteAddr),
+                        eq(DROID_SPI)))
+                .thenReturn(DROID_SPI);
+
+        IpSecSpiResponse spiResp =
+                mIpSecService.reserveSecurityParameterIndex(
+                        IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder());
+
+        mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
+
+        verify(mMockNetd)
+                .ipSecDeleteSecurityAssociation(
+                        eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
+    }
+
+    IpSecConfig buildIpSecConfig() throws Exception {
+        IpSecManager ipSecManager = new IpSecManager(mIpSecService);
+
+        // Mocking the netd to allocate SPI
+        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+                .thenReturn(DROID_SPI)
+                .thenReturn(DROID_SPI2);
+
+        IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+        IpSecAlgorithm authAlgo =
+                new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
+
+        /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
+        IpSecManager.SecurityParameterIndex outSpi =
+                ipSecManager.reserveSecurityParameterIndex(
+                        IpSecTransform.DIRECTION_OUT,
+                        NetworkUtils.numericToInetAddress(mRemoteAddr));
+        IpSecManager.SecurityParameterIndex inSpi =
+                ipSecManager.reserveSecurityParameterIndex(
+                        IpSecTransform.DIRECTION_IN,
+                        NetworkUtils.numericToInetAddress(mRemoteAddr));
+
+        IpSecConfig config = new IpSecConfig();
+        config.setSpiResourceId(IpSecTransform.DIRECTION_IN, inSpi.getResourceId());
+        config.setSpiResourceId(IpSecTransform.DIRECTION_OUT, outSpi.getResourceId());
+        config.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo);
+        config.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo);
+        config.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo);
+        config.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo);
+        config.setRemoteAddress(mRemoteAddr);
+        return config;
+    }
+
+    @Test
+    public void testCreateTransportModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = buildIpSecConfig();
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        anyString(),
+                        anyLong(),
+                        eq(DROID_SPI),
+                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+                        eq(AUTH_KEY),
+                        anyInt(),
+                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
+                        eq(CRYPT_KEY),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+        verify(mMockNetd)
+                .ipSecAddSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyInt(),
+                        eq(IpSecTransform.DIRECTION_IN),
+                        anyString(),
+                        anyString(),
+                        anyLong(),
+                        eq(DROID_SPI2),
+                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
+                        eq(AUTH_KEY),
+                        anyInt(),
+                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
+                        eq(CRYPT_KEY),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testDeleteTransportModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = buildIpSecConfig();
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+        mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+
+        verify(mMockNetd)
+                .ipSecDeleteSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        anyString(),
+                        eq(DROID_SPI));
+        verify(mMockNetd)
+                .ipSecDeleteSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        eq(IpSecTransform.DIRECTION_IN),
+                        anyString(),
+                        anyString(),
+                        eq(DROID_SPI2));
+    }
+
+    @Test
+    public void testApplyTransportModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = buildIpSecConfig();
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+
+        int resourceId = createTransformResp.resourceId;
+        mIpSecService.applyTransportModeTransform(pfd, resourceId);
+
+        verify(mMockNetd)
+                .ipSecApplyTransportModeTransform(
+                        eq(pfd.getFileDescriptor()),
+                        eq(resourceId),
+                        eq(IpSecTransform.DIRECTION_OUT),
+                        anyString(),
+                        anyString(),
+                        eq(DROID_SPI));
+        verify(mMockNetd)
+                .ipSecApplyTransportModeTransform(
+                        eq(pfd.getFileDescriptor()),
+                        eq(resourceId),
+                        eq(IpSecTransform.DIRECTION_IN),
+                        anyString(),
+                        anyString(),
+                        eq(DROID_SPI2));
+    }
+
+    @Test
+    public void testRemoveTransportModeTransform() throws Exception {
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
+        mIpSecService.removeTransportModeTransform(pfd, 1);
+
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+    }
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 23fee28..efc58cc 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -23,34 +23,28 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.INetd;
-import android.net.IpSecAlgorithm;
-import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransform;
-import android.net.IpSecTransformResponse;
 import android.net.IpSecUdpEncapResponse;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.support.test.filters.SmallTest;
 import android.system.ErrnoException;
 import android.system.Os;
+
 import java.io.FileDescriptor;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.UnknownHostException;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -62,13 +56,8 @@
 public class IpSecServiceTest {
 
     private static final int DROID_SPI = 0xD1201D;
-    private static final int DROID_SPI2 = DROID_SPI + 1;
     private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
     private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
-    private static final int TEST_UDP_ENCAP_PORT = 34567;
-
-    private static final String IPV4_LOOPBACK = "127.0.0.1";
-    private static final String IPV4_ADDR = "192.168.0.2";
 
     private static final InetAddress INADDR_ANY;
 
@@ -80,21 +69,6 @@
         }
     }
 
-    private static final int[] DIRECTIONS =
-            new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
-    private static final byte[] CRYPT_KEY = {
-        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
-        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
-    };
-    private static final byte[] AUTH_KEY = {
-        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
-        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
-    };
-
     Context mMockContext;
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
@@ -118,44 +92,6 @@
     }
 
     @Test
-    public void testIpSecServiceReserveSpi() throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        eq(IPV4_LOOPBACK),
-                        eq(DROID_SPI)))
-                .thenReturn(DROID_SPI);
-
-        IpSecSpiResponse spiResp =
-                mIpSecService.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
-        assertEquals(IpSecManager.Status.OK, spiResp.status);
-        assertEquals(DROID_SPI, spiResp.spi);
-    }
-
-    @Test
-    public void testReleaseSecurityParameterIndex() throws Exception {
-        when(mMockNetd.ipSecAllocateSpi(
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        eq(IPV4_LOOPBACK),
-                        eq(DROID_SPI)))
-                .thenReturn(DROID_SPI);
-
-        IpSecSpiResponse spiResp =
-                mIpSecService.reserveSecurityParameterIndex(
-                        IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder());
-
-        mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
-
-        verify(mMockNetd)
-                .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI));
-    }
-
-    @Test
     public void testReleaseInvalidSecurityParameterIndex() throws Exception {
         try {
             mIpSecService.releaseSecurityParameterIndex(1);
@@ -285,108 +221,6 @@
         }
     }
 
-    IpSecConfig buildIpSecConfig() throws Exception {
-        IpSecManager ipSecManager = new IpSecManager(mIpSecService);
-
-        // Mocking the netd to allocate SPI
-        when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
-                .thenReturn(DROID_SPI)
-                .thenReturn(DROID_SPI2);
-
-        IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
-        IpSecAlgorithm authAlgo =
-                new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8);
-
-        InetAddress localAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
-
-        /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */
-        IpSecManager.SecurityParameterIndex outSpi =
-                ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, localAddr);
-        IpSecManager.SecurityParameterIndex inSpi =
-                ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_IN, localAddr);
-
-        IpSecConfig ipSecConfig =
-                new IpSecTransform.Builder(mMockContext)
-                        .setSpi(IpSecTransform.DIRECTION_OUT, outSpi)
-                        .setSpi(IpSecTransform.DIRECTION_IN, inSpi)
-                        .setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo)
-                        .setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo)
-                        .setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo)
-                        .setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo)
-                        .getIpSecConfig();
-        return ipSecConfig;
-    }
-
-    @Test
-    public void testCreateTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
-
-        IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
-
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(DROID_SPI),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(DROID_SPI2),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-    }
-
-    @Test
-    public void testDeleteTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
-
-        IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
-
-        verify(mMockNetd)
-                .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        anyString(),
-                        eq(DROID_SPI));
-        verify(mMockNetd)
-                .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        eq(DROID_SPI2));
-    }
-
     @Test
     public void testDeleteInvalidTransportModeTransform() throws Exception {
         try {
@@ -397,39 +231,31 @@
     }
 
     @Test
-    public void testApplyTransportModeTransform() throws Exception {
-        IpSecConfig ipSecConfig = buildIpSecConfig();
-
-        IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
-
-        int resourceId = createTransformResp.resourceId;
-        mIpSecService.applyTransportModeTransform(pfd, resourceId);
-
-        verify(mMockNetd)
-                .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
-                        eq(resourceId),
-                        eq(IpSecTransform.DIRECTION_OUT),
-                        anyString(),
-                        anyString(),
-                        eq(DROID_SPI));
-        verify(mMockNetd)
-                .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
-                        eq(resourceId),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        eq(DROID_SPI2));
-    }
-
-    @Test
     public void testRemoveTransportModeTransform() throws Exception {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransform(pfd, 1);
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
+
+    @Test
+    public void testValidateIpAddresses() throws Exception {
+        String[] invalidAddresses =
+                new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
+        for (String address : invalidAddresses) {
+            try {
+                IpSecSpiResponse spiResp =
+                        mIpSecService.reserveSecurityParameterIndex(
+                                IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+                fail("Invalid address was passed through IpSecService validation: " + address);
+            } catch (IllegalArgumentException e) {
+            } catch (Exception e) {
+                fail(
+                        "Invalid InetAddress was not caught in validation: "
+                                + address
+                                + ", Exception: "
+                                + e);
+            }
+        }
+    }
 }
diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/tests/net/java/com/android/server/NsdServiceTest.java
index 2e49c98..b88c784 100644
--- a/tests/net/java/com/android/server/NsdServiceTest.java
+++ b/tests/net/java/com/android/server/NsdServiceTest.java
@@ -77,7 +77,10 @@
 
     @After
     public void tearDown() throws Exception {
-        mThread.quit();
+        if (mThread != null) {
+            mThread.quit();
+            mThread = null;
+        }
     }
 
     @Test
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 5f586a1..627a231 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1246,7 +1246,7 @@
         if (kIsDebug) {
             printf("Adding 9-patch info...\n");
         }
-        strcpy((char*)unknowns[p_index].name, "npTc");
+        memcpy((char*)unknowns[p_index].name, "npTc", 5);
         unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
         unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
         // TODO: remove the check below when everything works
@@ -1254,7 +1254,7 @@
 
         // automatically generated 9 patch outline data
         int chunk_size = sizeof(png_uint_32) * 6;
-        strcpy((char*)unknowns[o_index].name, "npOl");
+        memcpy((char*)unknowns[o_index].name, "npOl", 5);
         unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
         png_byte outputData[chunk_size];
         memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
@@ -1266,7 +1266,7 @@
         // optional optical inset / layout bounds data
         if (imageInfo.haveLayoutBounds) {
             int chunk_size = sizeof(png_uint_32) * 4;
-            strcpy((char*)unknowns[b_index].name, "npLb");
+            memcpy((char*)unknowns[b_index].name, "npLb", 5);
             unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
             memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
             unknowns[b_index].size = chunk_size;
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 6d6147d..33122dc 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -538,7 +538,7 @@
     if (kDebug) {
       diag->Note(DiagMessage() << "adding 9-patch info..");
     }
-    strcpy((char*)unknowns[pIndex].name, "npTc");
+    memcpy((char*)unknowns[pIndex].name, "npTc", 5);
     unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
     unknowns[pIndex].size = info->info9Patch.serializedSize();
     // TODO: remove the check below when everything works
@@ -546,7 +546,7 @@
 
     // automatically generated 9 patch outline data
     int chunkSize = sizeof(png_uint_32) * 6;
-    strcpy((char*)unknowns[oIndex].name, "npOl");
+    memcpy((char*)unknowns[oIndex].name, "npOl", 5);
     unknowns[oIndex].data = (png_byte*)calloc(chunkSize, 1);
     png_byte outputData[chunkSize];
     memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
@@ -558,7 +558,7 @@
     // optional optical inset / layout bounds data
     if (info->haveLayoutBounds) {
       int chunkSize = sizeof(png_uint_32) * 4;
-      strcpy((char*)unknowns[bIndex].name, "npLb");
+      memcpy((char*)unknowns[bIndex].name, "npLb", 5);
       unknowns[bIndex].data = (png_byte*)calloc(chunkSize, 1);
       memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
       unknowns[bIndex].size = chunkSize;
diff --git a/tools/incident_report/Android.bp b/tools/incident_report/Android.bp
index 6f21605..ab55dbd 100644
--- a/tools/incident_report/Android.bp
+++ b/tools/incident_report/Android.bp
@@ -32,7 +32,4 @@
     ],
 
     cflags: ["-Wno-unused-parameter"],
-
-    // b/34740546, work around clang-tidy segmentation fault.
-    tidy_checks: ["-modernize*"],
 }
diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp
index 7f8151f..1756e06 100644
--- a/tools/incident_section_gen/Android.bp
+++ b/tools/incident_section_gen/Android.bp
@@ -19,8 +19,6 @@
 // ==========================================================
 cc_binary_host {
     name: "incident-section-gen",
-    // b/34740546, work around clang-tidy segmentation fault.
-    tidy_checks: ["-modernize*"],
     cflags: [
         "-g",
         "-O0",