Merge changes I8e432950,Id325b8cf

* changes:
  Build android.test.base/legacy against SDK
  Fix visibility issues with android.test.mock.stubs
diff --git a/api/current.txt b/api/current.txt
index 45f38ae..10edfdc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40415,12 +40415,12 @@
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     method public int cancelDownload(android.telephony.mbms.DownloadRequest);
     method public void close();
-    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
-    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
     method public int download(android.telephony.mbms.DownloadRequest);
     method public java.io.File getTempFileRootDirectory();
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
-    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback);
     method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public void requestUpdateFileServices(java.util.List<java.lang.String>);
     method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
@@ -40448,10 +40448,10 @@
 
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
     method public void close();
-    method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
-    method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
+    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsStreamingSessionCallback);
     method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
-    method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
+    method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
   public class NeighboringCellInfo implements android.os.Parcelable {
@@ -41279,20 +41279,23 @@
 package android.telephony.mbms {
 
   public final class DownloadRequest implements android.os.Parcelable {
-    method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
     method public int describeContents();
+    method public android.net.Uri getDestinationUri();
     method public java.lang.String getFileServiceId();
     method public static int getMaxAppIntentSize();
     method public static int getMaxDestinationUriSize();
     method public android.net.Uri getSourceUri();
     method public int getSubscriptionId();
+    method public byte[] toByteArray();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder(android.net.Uri);
+    ctor public DownloadRequest.Builder(android.net.Uri, android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
+    method public static android.telephony.mbms.DownloadRequest.Builder fromDownloadRequest(android.telephony.mbms.DownloadRequest);
+    method public static android.telephony.mbms.DownloadRequest.Builder fromSerializedRequest(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
@@ -41388,10 +41391,10 @@
     method public java.util.Date getSessionStartTime();
   }
 
-  public class StreamingService {
+  public class StreamingService implements java.lang.AutoCloseable {
+    method public void close();
     method public android.telephony.mbms.StreamingServiceInfo getInfo();
     method public android.net.Uri getPlaybackUri();
-    method public void stopStreaming();
     field public static final int BROADCAST_METHOD = 1; // 0x1
     field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
     field public static final int REASON_END_OF_SESSION = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index d2c6bd0..e3336db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5287,12 +5287,7 @@
 
 package android.telephony.mbms {
 
-  public final class DownloadRequest implements android.os.Parcelable {
-    method public byte[] getOpaqueData();
-  }
-
   public static class DownloadRequest.Builder {
-    method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
   }
 
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ab075ee..238cb65 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -167,6 +167,8 @@
             } else if (opt.equals("--no_window_animation")
                     || opt.equals("--no-window-animation")) {
                 instrument.noWindowAnimation = true;
+            } else if (opt.equals("--no-hidden-api-checks")) {
+                instrument.disableHiddenApiChecks = true;
             } else if (opt.equals("--user")) {
                 instrument.userId = parseUserArg(nextArgRequired());
             } else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index b69ef1c..432e890 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -52,12 +52,17 @@
     public boolean rawMode = false;
     public boolean proto = false;
     public boolean noWindowAnimation = false;
+    public boolean disableHiddenApiChecks = false;
     public String abi = null;
     public int userId = UserHandle.USER_CURRENT;
     public Bundle args = new Bundle();
     // Required
     public String componentNameArg;
 
+    // Disable hidden API checks for the newly started instrumentation.
+    // Must be kept in sync with ActivityManagerService.
+    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
     /**
      * Construct the instrument command runner.
      */
@@ -416,7 +421,8 @@
             }
 
             // Start the instrumentation
-            if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+            int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+            if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
                         abi)) {
                 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
             }
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
deleted file mode 100644
index 955e58e..0000000
--- a/cmds/webview_zygote/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := webview_zygote
-
-LOCAL_SRC_FILES := webview_zygote.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libandroid_runtime \
-	libbinder \
-	liblog \
-	libcutils \
-	libutils
-
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-
-LOCAL_INIT_RC := webview_zygote32.rc
-
-# Always include the 32-bit version of webview_zygote. If the target is 64-bit,
-# also include the 64-bit webview_zygote.
-ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
-	LOCAL_INIT_RC += webview_zygote64.rc
-endif
-
-LOCAL_MULTILIB := both
-
-LOCAL_MODULE_STEM_32 := webview_zygote32
-LOCAL_MODULE_STEM_64 := webview_zygote64
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/webview_zygote/webview_zygote.cpp b/cmds/webview_zygote/webview_zygote.cpp
deleted file mode 100644
index 88fee64..0000000
--- a/cmds/webview_zygote/webview_zygote.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#define LOG_TAG "WebViewZygote"
-
-#include <sys/prctl.h>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-class WebViewRuntime : public AndroidRuntime {
-public:
-    WebViewRuntime(char* argBlockStart, size_t argBlockSize)
-        : AndroidRuntime(argBlockStart, argBlockSize) {}
-
-    ~WebViewRuntime() override {}
-
-    void onStarted() override {
-        // Nothing to do since this is a zygote server.
-    }
-
-    void onVmCreated(JNIEnv*) override {
-        // Nothing to do when the VM is created in the zygote.
-    }
-
-    void onZygoteInit() override {
-        // Called after a new process is forked.
-        sp<ProcessState> proc = ProcessState::self();
-        proc->startThreadPool();
-    }
-
-    void onExit(int code) override {
-        IPCThreadState::self()->stopProcess();
-        AndroidRuntime::onExit(code);
-    }
-};
-
-}  // namespace android
-
-int main(int argc, char* const argv[]) {
-    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
-        return 12;
-    }
-
-    size_t argBlockSize = 0;
-    for (int i = 0; i < argc; ++i) {
-        argBlockSize += strlen(argv[i]) + 1;
-    }
-
-    android::WebViewRuntime runtime(argv[0], argBlockSize);
-    runtime.addOption("-Xzygote");
-
-    android::Vector<android::String8> args;
-    runtime.start("com.android.internal.os.WebViewZygoteInit", args, /*zygote=*/ true);
-}
diff --git a/cmds/webview_zygote/webview_zygote32.rc b/cmds/webview_zygote/webview_zygote32.rc
deleted file mode 100644
index b7decc8..0000000
--- a/cmds/webview_zygote/webview_zygote32.rc
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-service webview_zygote32 /system/bin/webview_zygote32
-    user webview_zygote
-    socket webview_zygote stream 660 webview_zygote system
-
-on property:init.svc.zygote=stopped
-    stop webview_zygote32
diff --git a/cmds/webview_zygote/webview_zygote64.rc b/cmds/webview_zygote/webview_zygote64.rc
deleted file mode 100644
index 2935b28..0000000
--- a/cmds/webview_zygote/webview_zygote64.rc
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-service webview_zygote64 /system/bin/webview_zygote64
-    user webview_zygote
-    socket webview_zygote stream 660 webview_zygote system
-
-on property:init.svc.zygote=stopped
-    stop webview_zygote64
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 80fc8e3..8ea81a4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -35,6 +35,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1459,7 +1460,10 @@
      * @hide
      */
     public boolean isAllowedToUseHiddenApi() {
-        return isSystemApp();
+        boolean whitelisted =
+                SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+        return isSystemApp() || // TODO get rid of this once the whitelist has been populated
+                (whitelisted && (isSystemApp() || isUpdatedSystemApp()));
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 472ac57..3a8a254 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1602,8 +1602,12 @@
         /** The hardware returned an error. */
         public static final int ERROR_HARDWARE_ERROR = -31;
 
+        /** The NAT-T destination port for IPsec */
         public static final int NATT_PORT = 4500;
 
+        /** The minimum interval in seconds between keepalive packet transmissions */
+        public static final int MIN_INTERVAL = 10;
+
         private final Network mNetwork;
         private final PacketKeepaliveCallback mCallback;
         private final Looper mLooper;
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 08d4ff5..7436ad0 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -16,8 +16,8 @@
 
 package android.net;
 
-import android.system.OsConstants;
-import android.net.ConnectivityManager;
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+
 import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,13 +25,10 @@
 import android.util.Log;
 
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
-import static android.net.ConnectivityManager.PacketKeepalive.*;
-
 /**
  * Represents the actual packets that are sent by the
  * {@link android.net.ConnectivityManager.PacketKeepalive} API.
@@ -98,13 +95,6 @@
             InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
             throws InvalidPacketException {
 
-        // FIXME: remove this and actually support IPv6 keepalives
-        if (srcAddress instanceof Inet6Address && dstAddress instanceof Inet6Address) {
-            // Optimistically returning an IPv6 Keepalive Packet with no data,
-            // which currently only works on cellular
-            return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, new byte[0]);
-        }
-
         if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
             throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
         }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index bc4d955..c94ae93 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,7 +21,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 668cfba..d06a20b 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -463,7 +463,7 @@
      */
     public static int onWebViewProviderChanged(PackageInfo packageInfo) {
         String[] nativeLibs = null;
-        String originalSourceDir = packageInfo.applicationInfo.sourceDir;
+        ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo);
         try {
             fixupStubApplicationInfo(packageInfo.applicationInfo,
                                      AppGlobals.getInitialApplication().getPackageManager());
@@ -474,7 +474,7 @@
             Log.e(LOGTAG, "error preparing webview native library", t);
         }
 
-        WebViewZygote.onWebViewProviderChanged(packageInfo, originalSourceDir);
+        WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo);
 
         return prepareWebViewInSystemServer(nativeLibs);
     }
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 0204dff..d2923c4 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -17,31 +17,25 @@
 package android.webkit;
 
 import android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.os.Build;
-import android.os.SystemService;
+import android.os.ChildZygoteProcess;
+import android.os.Process;
 import android.os.ZygoteProcess;
 import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.TimeoutException;
 
 /** @hide */
 public class WebViewZygote {
     private static final String LOGTAG = "WebViewZygote";
 
-    private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
-    private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
-    private static final String WEBVIEW_ZYGOTE_SOCKET = "webview_zygote";
-
     /**
      * Lock object that protects all other static members.
      */
@@ -52,14 +46,7 @@
      * is not running or is not connected.
      */
     @GuardedBy("sLock")
-    private static ZygoteProcess sZygote;
-
-    /**
-     * Variable that allows us to determine whether the WebView zygote Service has already been
-     * started.
-     */
-    @GuardedBy("sLock")
-    private static boolean sStartedService = false;
+    private static ChildZygoteProcess sZygote;
 
     /**
      * Information about the selected WebView package. This is set from #onWebViewProviderChanged().
@@ -68,11 +55,11 @@
     private static PackageInfo sPackage;
 
     /**
-     * Cache key for the selected WebView package's classloader. This is set from
+     * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from
      * #onWebViewProviderChanged().
      */
     @GuardedBy("sLock")
-    private static String sPackageCacheKey;
+    private static ApplicationInfo sPackageOriginalAppInfo;
 
     /**
      * Flag for whether multi-process WebView is enabled. If this is false, the zygote
@@ -85,7 +72,7 @@
         synchronized (sLock) {
             if (sZygote != null) return sZygote;
 
-            waitForServiceStartAndConnect();
+            connectToZygoteIfNeededLocked();
             return sZygote;
         }
     }
@@ -107,82 +94,43 @@
             sMultiprocessEnabled = enabled;
 
             // When toggling between multi-process being on/off, start or stop the
-            // service. If it is enabled and the zygote is not yet started, bring up the service.
-            // Otherwise, bring down the service. The name may be null if the package
-            // information has not yet been resolved.
-            final String serviceName = getServiceNameLocked();
-            if (serviceName == null) return;
-
+            // zygote. If it is enabled and the zygote is not yet started, launch it.
+            // Otherwise, kill it. The name may be null if the package information has
+            // not yet been resolved.
             if (enabled) {
-                if (!sStartedService) {
-                    SystemService.start(serviceName);
-                    sStartedService = true;
-                }
+                connectToZygoteIfNeededLocked();
             } else {
-                SystemService.stop(serviceName);
-                sStartedService = false;
-                sZygote = null;
+                stopZygoteLocked();
             }
         }
     }
 
-    public static void onWebViewProviderChanged(PackageInfo packageInfo, String cacheKey) {
+    public static void onWebViewProviderChanged(PackageInfo packageInfo,
+                                                ApplicationInfo originalAppInfo) {
         synchronized (sLock) {
             sPackage = packageInfo;
-            sPackageCacheKey = cacheKey;
+            sPackageOriginalAppInfo = originalAppInfo;
 
             // If multi-process is not enabled, then do not start the zygote service.
             if (!sMultiprocessEnabled) {
                 return;
             }
 
-            final String serviceName = getServiceNameLocked();
-            sZygote = null;
-
-            // The service may enter the RUNNING state before it opens the socket,
-            // so connectToZygoteIfNeededLocked() may still fail.
-            if (SystemService.isStopped(serviceName)) {
-                SystemService.start(serviceName);
-            } else {
-                SystemService.restart(serviceName);
-            }
-            sStartedService = true;
-        }
-    }
-
-    private static void waitForServiceStartAndConnect() {
-        if (!sStartedService) {
-            throw new AndroidRuntimeException("Tried waiting for the WebView Zygote Service to " +
-                    "start running without first starting the service.");
-        }
-
-        String serviceName;
-        synchronized (sLock) {
-            serviceName = getServiceNameLocked();
-        }
-        try {
-            SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
-        } catch (TimeoutException e) {
-            Log.e(LOGTAG, "Timed out waiting for " + serviceName);
-            return;
-        }
-
-        synchronized (sLock) {
-            connectToZygoteIfNeededLocked();
+            stopZygoteLocked();
         }
     }
 
     @GuardedBy("sLock")
-    private static String getServiceNameLocked() {
-        if (sPackage == null)
-            return null;
-
-        if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
-                    sPackage.applicationInfo.primaryCpuAbi)) {
-            return WEBVIEW_ZYGOTE_SERVICE_64;
+    private static void stopZygoteLocked() {
+        if (sZygote != null) {
+            // Close the connection and kill the zygote process. This will not cause
+            // child processes to be killed by itself. But if this is called in response to
+            // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater
+            // will kill all processes that depend on the WebView package.
+            sZygote.close();
+            Process.killProcess(sZygote.getPid());
+            sZygote = null;
         }
-
-        return WEBVIEW_ZYGOTE_SERVICE_32;
     }
 
     @GuardedBy("sLock")
@@ -196,14 +144,17 @@
             return;
         }
 
-        final String serviceName = getServiceNameLocked();
-        if (!SystemService.isRunning(serviceName)) {
-            Log.e(LOGTAG, serviceName + " is not running");
-            return;
-        }
-
         try {
-            sZygote = new ZygoteProcess(WEBVIEW_ZYGOTE_SOCKET, null);
+            sZygote = Process.zygoteProcess.startChildZygote(
+                    "com.android.internal.os.WebViewZygoteInit",
+                    "webview_zygote",
+                    Process.WEBVIEW_ZYGOTE_UID,
+                    Process.WEBVIEW_ZYGOTE_UID,
+                    null,  // gids
+                    0,  // runtimeFlags
+                    "webview_zygote",  // seInfo
+                    sPackage.applicationInfo.primaryCpuAbi,  // abi
+                    null);  // instructionSet
 
             // All the work below is usually done by LoadedApk, but the zygote can't talk to
             // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
@@ -218,14 +169,21 @@
             final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
                     TextUtils.join(File.pathSeparator, zipPaths);
 
-            ZygoteProcess.waitForConnectionToZygote(WEBVIEW_ZYGOTE_SOCKET);
+            // In the case where the ApplicationInfo has been modified by the stub WebView,
+            // we need to use the original ApplicationInfo to determine what the original classpath
+            // would have been to use as a cache key.
+            LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null);
+            final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
+                    TextUtils.join(File.pathSeparator, zipPaths);
+
+            ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
 
             Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
-            sZygote.preloadPackageForAbi(zip, librarySearchPath, sPackageCacheKey,
+            sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey,
                                          Build.SUPPORTED_ABIS[0]);
         } catch (Exception e) {
-            Log.e(LOGTAG, "Error connecting to " + serviceName, e);
-            sZygote = null;
+            Log.e(LOGTAG, "Error connecting to webview zygote", e);
+            stopZygoteLocked();
         }
     }
 }
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index b38c851..32b580c 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -18,9 +18,11 @@
 
 import android.app.ApplicationLoaders;
 import android.net.LocalSocket;
+import android.net.LocalServerSocket;
 import android.os.Build;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.WebViewFactory;
@@ -118,18 +120,35 @@
     }
 
     public static void main(String argv[]) {
-        sServer = new WebViewZygoteServer();
+        Log.i(TAG, "Starting WebViewZygoteInit");
 
-        // Zygote goes into its own process group.
-        try {
-            Os.setpgid(0, 0);
-        } catch (ErrnoException ex) {
-            throw new RuntimeException("Failed to setpgid(0,0)", ex);
+        String socketName = null;
+        for (String arg : argv) {
+            Log.i(TAG, arg);
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+                socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+            }
         }
+        if (socketName == null) {
+            throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
+        }
+
+        try {
+            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+        }
+
+        sServer = new WebViewZygoteServer();
 
         final Runnable caller;
         try {
-            sServer.registerServerSocketFromEnv("webview_zygote");
+            sServer.registerServerSocketAtAbstractName(socketName);
+
+            // Add the abstract socket to the FD whitelist so that the native zygote code
+            // can properly detach it after forking.
+            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
             // The select loop returns early in the child process after a fork and
             // loops forever in the zygote.
             caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 7778892..bdb5f99 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -60,6 +60,7 @@
     private static final int ALLOW_PERMISSIONS = 0x04;
     private static final int ALLOW_APP_CONFIGS = 0x08;
     private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
+    private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x20;
     private static final int ALLOW_ALL = ~0;
 
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
@@ -134,6 +135,9 @@
     // These are the permitted backup transport service components
     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
 
+    // Package names that are exempted from private API blacklisting
+    final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
+
     // These are the packages of carrier-associated apps which should be disabled until used until
     // a SIM is inserted which grants carrier privileges to that carrier app.
     final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
@@ -204,6 +208,10 @@
         return mSystemUserBlacklistedApps;
     }
 
+    public ArraySet<String> getHiddenApiWhitelistedApps() {
+        return mHiddenApiPackageWhitelist;
+    }
+
     public ArraySet<ComponentName> getDefaultVrComponents() {
         return mDefaultVrComponents;
     }
@@ -327,6 +335,7 @@
             boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
             boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
             boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
+            boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -569,6 +578,15 @@
                     XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
                     readPrivAppPermissions(parser);
+                } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    if (pkgname == null) {
+                        Slog.w(TAG, "<hidden-api-whitelisted-app> without package in " + permFile
+                                + " at " + parser.getPositionDescription());
+                    } else {
+                        mHiddenApiPackageWhitelist.add(pkgname);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     XmlUtils.skipCurrentTag(parser);
                     continue;
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index b2c6840..936ad22 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -39,3 +39,11 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hiddenapi-package-whitelist.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
new file mode 100644
index 0000000..54d8a23
--- /dev/null
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!--
+This XML file declares which system apps should be exempted from the hidden API blacklisting, i.e.
+which apps should be allowed to access the entire private API.
+-->
+
+<config>
+  <hidden-api-whitelisted-app package="com.android.providers.contacts" />
+</config>
+
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 16c2b68..0af187c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -147,6 +147,7 @@
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
         <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 2370273..fdbb260 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -76,6 +76,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        if (mController == null) return;
         if (listening) {
             mController.addCallback(mSignalCallback);
         } else {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 5e5eacb..ce78665 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
+import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneStateListener;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
@@ -96,7 +97,8 @@
         IPhoneStateListener callback;
         IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
 
-        int callerUserId;
+        int callerUid;
+        int callerPid;
 
         int events;
 
@@ -120,7 +122,7 @@
                     + " callback=" + callback
                     + " onSubscriptionsChangedListenererCallback="
                                             + onSubscriptionsChangedListenerCallback
-                    + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+                    + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
                     + " events=" + Integer.toHexString(events)
                     + " canReadPhoneState=" + canReadPhoneState + "}";
         }
@@ -374,6 +376,8 @@
     public void addOnSubscriptionsChangedListener(String callingPackage,
             IOnSubscriptionsChangedListener callback) {
         int callerUserId = UserHandle.getCallingUserId();
+        mContext.getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
                 + " callerUserId="  + callerUserId + " callback=" + callback
@@ -408,7 +412,8 @@
 
             r.onSubscriptionsChangedListenerCallback = callback;
             r.callingPackage = callingPackage;
-            r.callerUserId = callerUserId;
+            r.callerUid = Binder.getCallingUid();
+            r.callerPid = Binder.getCallingPid();
             r.events = 0;
             r.canReadPhoneState = true; // permission has been enforced above
             if (DBG) {
@@ -479,6 +484,8 @@
     private void listen(String callingPackage, IPhoneStateListener callback, int events,
             boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
+        mContext.getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
                 + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -503,6 +510,7 @@
                 }
             }
 
+            int phoneId = SubscriptionManager.getPhoneId(subId);
             synchronized (mRecords) {
                 // register
                 IBinder b = callback.asBinder();
@@ -514,7 +522,8 @@
 
                 r.callback = callback;
                 r.callingPackage = callingPackage;
-                r.callerUserId = callerUserId;
+                r.callerUid = Binder.getCallingUid();
+                r.callerPid = Binder.getCallingPid();
                 boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
                         | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
                 r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
@@ -525,9 +534,7 @@
                  } else {//APP specify subID
                     r.subId = subId;
                 }
-                r.phoneId = SubscriptionManager.getPhoneId(r.subId);
-
-                int phoneId = r.phoneId;
+                r.phoneId = phoneId;
                 r.events = events;
                 if (DBG) {
                     log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -572,8 +579,10 @@
                         try {
                             if (DBG_LOC) log("listen: mCellLocation = "
                                     + mCellLocation[phoneId]);
-                            r.callback.onCellLocationChanged(
-                                    new Bundle(mCellLocation[phoneId]));
+                            if (checkLocationAccess(r)) {
+                                r.callback.onCellLocationChanged(
+                                        new Bundle(mCellLocation[phoneId]));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -619,7 +628,9 @@
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                     + mCellInfo.get(phoneId));
-                            r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            if (checkLocationAccess(r)) {
+                                r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -1013,14 +1024,14 @@
             log("notifyCellInfoForSubscriber: subId=" + subId
                 + " cellInfo=" + cellInfo);
         }
-
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCellInfo.set(phoneId, cellInfo);
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
-                            idMatch(r.subId, subId, phoneId)) {
+                            idMatch(r.subId, subId, phoneId) &&
+                            checkLocationAccess(r)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1103,8 +1114,8 @@
             log("notifyCallForwardingChangedForSubscriber: subId=" + subId
                 + " cfi=" + cfi);
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCallForwarding[phoneId] = cfi;
                 for (Record r : mRecords) {
@@ -1131,8 +1142,8 @@
         if (!checkNotifyPermission("notifyDataActivity()" )) {
             return;
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mDataActivity[phoneId] = state;
                 for (Record r : mRecords) {
@@ -1173,8 +1184,8 @@
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 boolean modified = false;
                 if (state == TelephonyManager.DATA_CONNECTED) {
@@ -1297,13 +1308,14 @@
             log("notifyCellLocationForSubscriber: subId=" + subId
                 + " cellLocation=" + cellLocation);
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCellLocation[phoneId] = cellLocation;
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
-                            idMatch(r.subId, subId, phoneId)) {
+                            idMatch(r.subId, subId, phoneId) &&
+                            checkLocationAccess(r)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1747,10 +1759,11 @@
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
-            valid = r.callerUserId ==  foregroundUser && r.matchPhoneStateListenerEvent(events);
+            valid = UserHandle.getUserId(r.callerUid) == foregroundUser
+                    && r.matchPhoneStateListenerEvent(events);
             if (DBG | DBG_LOC) {
                 log("validateEventsAndUserLocked: valid=" + valid
-                        + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+                        + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
                         + " r.events=" + r.events + " events=" + events);
             }
         } finally {
@@ -1782,6 +1795,16 @@
         }
     }
 
+    private boolean checkLocationAccess(Record r) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            return LocationAccessPolicy.canAccessCellLocation(mContext,
+                    r.callingPackage, r.callerUid, r.callerPid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     private void checkPossibleMissNotify(Record r, int phoneId) {
         int events = r.events;
 
@@ -1829,7 +1852,9 @@
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
                             + mCellInfo.get(phoneId));
                 }
-                r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                if (checkLocationAccess(r)) {
+                    r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                }
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
@@ -1877,7 +1902,9 @@
             try {
                 if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
                         + mCellLocation[phoneId]);
-                r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+                if (checkLocationAccess(r)) {
+                    r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+                }
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4700057..6c60b74 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -533,6 +533,10 @@
     // How long we wait until we timeout on key dispatching during instrumentation.
     static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
 
+    // Disable hidden API checks for the newly started instrumentation.
+    // Must be kept in sync with Am.
+    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
     // How long to wait in getAssistContextExtras for the activity and foreground services
     // to respond with the result.
     static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -3813,6 +3817,13 @@
 
     private final void startProcessLocked(ProcessRecord app, String hostingType,
             String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
+        startProcessLocked(app, hostingType, hostingNameStr, false /* disableHiddenApiChecks */,
+                null /* abiOverride */, null /* entryPoint */, null /* entryPointArgs */);
+    }
+
+    private final void startProcessLocked(ProcessRecord app, String hostingType,
+            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride,
+            String entryPoint, String[] entryPointArgs) {
         long startTime = SystemClock.elapsedRealtime();
         if (app.pid > 0 && app.pid != MY_PID) {
             checkTime(startTime, "startProcess: removing from pids map");
@@ -3933,7 +3944,9 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
-            if (!app.info.isAllowedToUseHiddenApi() && !mHiddenApiBlacklist.isDisabled()) {
+            if (!app.info.isAllowedToUseHiddenApi() &&
+                    !disableHiddenApiChecks &&
+                    !mHiddenApiBlacklist.isDisabled()) {
                 // This app is not allowed to use undocumented and private APIs, or blacklisting is
                 // enabled. Set up its runtime with the appropriate flag.
                 runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
@@ -12456,6 +12469,12 @@
 
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             String abiOverride) {
+        return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
+                abiOverride);
+    }
+
+    final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
+            boolean disableHiddenApiChecks, String abiOverride) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -12487,8 +12506,8 @@
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
             startProcessLocked(app, "added application",
-                    customProcess != null ? customProcess : app.processName, abiOverride,
-                    null /* entryPoint */, null /* entryPointArgs */);
+                    customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
+                    abiOverride, null /* entryPoint */, null /* entryPointArgs */);
         }
 
         return app;
@@ -20153,7 +20172,10 @@
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
                     "start instr");
-            ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
+            boolean disableHiddenApiChecks =
+                    (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+            ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+                    abiOverride);
             app.instr = activeInstr;
             activeInstr.mFinished = false;
             activeInstr.mRunningProcesses.add(app);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 59c0ed1..254f403 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2729,7 +2729,7 @@
             pw.println("          specified then send to all users.");
             pw.println("      --receiver-permission <PERMISSION>: Require receiver to hold permission.");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
-            pw.println("          [--user <USER_ID> | current]");
+            pw.println("          [--user <USER_ID> | current] [--no-hidden-api-checks]");
             pw.println("          [--no-window-animation] [--abi <ABI>] <COMPONENT>");
             pw.println("      Start an Instrumentation.  Typically this target <COMPONENT> is in the");
             pw.println("      form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2744,6 +2744,7 @@
             pw.println("          test runners.");
             pw.println("      --user <USER_ID> | current: Specify user instrumentation runs in;");
             pw.println("          current user if not specified.");
+            pw.println("      --no-hidden-api-checks: disable restrictions on use of hidden API.");
             pw.println("      --no-window-animation: turn off window animations while running.");
             pw.println("      --abi <ABI>: Launch the instrumented process with the selected ABI.");
             pw.println("          This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index d24f9c9..0f8fc17 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -172,7 +172,7 @@
         }
 
         private int checkInterval() {
-            return mInterval >= 10 ? SUCCESS : ERROR_INVALID_INTERVAL;
+            return mInterval >= MIN_INTERVAL ? SUCCESS : ERROR_INVALID_INTERVAL;
         }
 
         private int isValid() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a24f97e..505480e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -285,7 +285,6 @@
         int delta = add ? +1 : -1;
         switch (request.type) {
             case REQUEST:
-            case TRACK_DEFAULT:
                 mNumRequestNetworkRequests += delta;
                 break;
 
@@ -294,6 +293,7 @@
                 mNumBackgroundNetworkRequests += delta;
                 break;
 
+            case TRACK_DEFAULT:
             case LISTEN:
                 break;
 
@@ -384,12 +384,15 @@
 
     /**
      * Returns whether the network is a background network. A network is a background network if it
-     * is satisfying no foreground requests and at least one background request. (If it did not have
-     * a background request, it would be a speculative network that is only being kept up because
-     * it might satisfy a request if it validated).
+     * does not have the NET_CAPABILITY_FOREGROUND capability, which implies it is satisfying no
+     * foreground request, is not lingering (i.e. kept for a while after being outscored), and is
+     * not a speculative network (i.e. kept pending validation when validation would have it
+     * outscore another foreground network). That implies it is being kept up by some background
+     * request (otherwise it would be torn down), maybe the mobile always-on request.
      */
     public boolean isBackgroundNetwork() {
-        return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
+        return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0
+                && !isLingering();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4997635..900e188 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9851,6 +9851,10 @@
             // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
             // trade-off worth doing to save boot time work.
             int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
+            if (compilationReason == REASON_FIRST_BOOT) {
+                // TODO: This doesn't cover the upgrade case, we should check for this too.
+                dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+            }
             int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
                     pkg.packageName,
                     pkgCompilationReason,
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b888ec2..f6e863b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -847,6 +847,17 @@
             }
         }
 
+        public void scheduleTimeoutLocked() {
+            // If we didn't reset it right away, do so after we couldn't connect to
+            // it for an extended amount of time to avoid having a black wallpaper.
+            final Handler fgHandler = FgThread.getHandler();
+            fgHandler.removeCallbacks(mResetRunnable);
+            fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
+            if (DEBUG_LIVE) {
+                Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+            }
+        }
+
         private void processDisconnect(final ServiceConnection connection) {
             synchronized (mLock) {
                 // The wallpaper disappeared.  If this isn't a system-default one, track
@@ -871,13 +882,13 @@
                         } else {
                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
 
-                            // If we didn't reset it right away, do so after we couldn't connect to
-                            // it for an extended amount of time to avoid having a black wallpaper.
-                            final Handler fgHandler = FgThread.getHandler();
-                            fgHandler.removeCallbacks(mResetRunnable);
-                            fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
-                            if (DEBUG_LIVE) {
-                                Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
+                            clearWallpaperComponentLocked(mWallpaper);
+                            if (bindWallpaperComponentLocked(
+                                    wpService, false, false, mWallpaper, null)) {
+                                mWallpaper.connection.scheduleTimeoutLocked();
+                            } else {
+                                Slog.w(TAG, "Reverting to built-in wallpaper!");
+                                clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
                             }
                         }
                         final String flattened = wpService.flattenToString();
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index bf769ed..1e334b8 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -304,6 +304,6 @@
 
     // flags declaring we want extra info from the package manager for webview providers
     private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
-            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
-            | PackageManager.MATCH_ANY_USER;
+            | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
+            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
 }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index f53eb15..763dffb 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -34,6 +34,7 @@
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
 import android.hardware.soundtrigger.SoundTriggerModule;
+import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -880,21 +881,26 @@
     }
 
     private void initializeTelephonyAndPowerStateListeners() {
-        // Get the current call state synchronously for the first recognition.
-        mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
+        long token = Binder.clearCallingIdentity();
+        try {
+            // Get the current call state synchronously for the first recognition.
+            mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
 
-        // Register for call state changes when the first call to start recognition occurs.
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+            // Register for call state changes when the first call to start recognition occurs.
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
-        // Register for power saver mode changes when the first call to start recognition
-        // occurs.
-        if (mPowerSaveModeListener == null) {
-            mPowerSaveModeListener = new PowerSaveModeListener();
-            mContext.registerReceiver(mPowerSaveModeListener,
-                    new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+            // Register for power saver mode changes when the first call to start recognition
+            // occurs.
+            if (mPowerSaveModeListener == null) {
+                mPowerSaveModeListener = new PowerSaveModeListener();
+                mContext.registerReceiver(mPowerSaveModeListener,
+                        new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+            }
+            mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
+                    .batterySaverEnabled;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
-                .batterySaverEnabled;
     }
 
     // Sends an error callback to all models with a valid registered callback.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f360680..de75ac4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1855,7 +1855,7 @@
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
-        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..b362df9
--- /dev/null
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.SparseBooleanArray;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+    /**
+     * API to determine if the caller has permissions to get cell location.
+     *
+     * @param pkgName Package name of the application requesting access
+     * @param uid The uid of the package
+     * @param pid The pid of the package
+     * @return boolean true or false if permissions is granted
+     */
+    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+            int uid, int pid) throws SecurityException {
+        Trace.beginSection("TelephonyLocationCheck");
+        try {
+            // Always allow the phone process to access location. This avoid breaking legacy code
+            // that rely on public-facing APIs to access cell location, and it doesn't create a
+            // info leak risk because the cell location is stored in the phone process anyway.
+            if (uid == Process.PHONE_UID) {
+                return true;
+            }
+
+            // We always require the location permission and also require the
+            // location mode to be on for non-legacy apps. Legacy apps are
+            // required to be in the foreground to at least mitigate the case
+            // where a legacy app the user is not using tracks their location.
+            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+            // ACCESS_COARSE_LOCATION.
+
+            if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
+                    PackageManager.PERMISSION_DENIED) {
+                return false;
+            }
+            final int opCode = AppOpsManager.permissionToOpCode(
+                    Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
+                    && !isLegacyForeground(context, pkgName, uid)) {
+                return false;
+            }
+            // If the user or profile is current, permission is granted.
+            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+        int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+        return locationMode != Settings.Secure.LOCATION_MODE_OFF
+                && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+    }
+
+    private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
+            int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0)
+                    .targetSdkVersion <= Build.VERSION_CODES.O) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+
+    private static boolean isForegroundApp(@NonNull Context context, int uid) {
+        final ActivityManager am = context.getSystemService(ActivityManager.class);
+        return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+    }
+
+    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            final int currentUser = ActivityManager.getCurrentUser();
+            final int callingUserId = UserHandle.getUserId(uid);
+            if (callingUserId == currentUser) {
+                return true;
+            } else {
+                List<UserInfo> userProfiles = context.getSystemService(
+                        UserManager.class).getProfiles(currentUser);
+                for (UserInfo user : userProfiles) {
+                    if (user.id == callingUserId) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index da3d87b..ce1b80c 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -30,7 +30,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
@@ -53,6 +52,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -107,11 +107,8 @@
     /**
      * {@link Uri} extra that Android will attach to the intent supplied via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
-     * Indicates the location of the successfully downloaded file within the temp file root set
-     * via {@link #setTempFileRootDirectory(File)}.
-     * While you may use this file in-place, it is highly encouraged that you move
-     * this file to a different location after receiving the download completion intent, as this
-     * file resides within the temp file directory.
+     * Indicates the location of the successfully downloaded file within the directory that the
+     * app provided via the builder.
      *
      * Will always be set to a non-null value if
      * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
@@ -220,6 +217,8 @@
      */
     public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
 
+    private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
     private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
 
     private final Context mContext;
@@ -236,23 +235,20 @@
     private final Map<DownloadStateCallback, InternalDownloadStateCallback>
             mInternalDownloadCallbacks = new HashMap<>();
 
-    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
-            int subscriptionId, Handler handler) {
+    private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+            MbmsDownloadSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+        mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
     }
 
     /**
      * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+     * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
      */
     public static MbmsDownloadSession create(@NonNull Context context,
-            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
@@ -279,24 +275,24 @@
      * {@link MbmsDownloadSession} that you received before calling this method again.
      *
      * @param context The instance of {@link Context} to use
-     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The data subscription ID to use
-     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @param callback A callback to get asynchronous error messages and file service updates.
      * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
      * setup.
      */
     public static @Nullable MbmsDownloadSession create(@NonNull Context context,
-            final @NonNull MbmsDownloadSessionCallback callback,
-            int subscriptionId, @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsDownloadSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot have two active instances");
         }
         MbmsDownloadSession session =
-                new MbmsDownloadSession(context, callback, subscriptionId, handler);
+                new MbmsDownloadSession(context, executor, subscriptionId, callback);
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -500,6 +496,10 @@
      * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
      * file root directory.
      *
+     * If the {@link DownloadRequest} has a destination that is not on the same filesystem as the
+     * temp file directory provided via {@link #getTempFileRootDirectory()}, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
      * Asynchronous errors through the callback may include any error not specific to the
      * streaming use-case.
      * @param request The request that specifies what should be downloaded.
@@ -522,6 +522,8 @@
             setTempFileRootDirectory(tempRootDirectory);
         }
 
+        checkDownloadRequestDestination(request);
+
         try {
             int result = downloadService.download(request);
             if (result == MbmsErrors.SUCCESS) {
@@ -568,21 +570,21 @@
      * this method will throw an {@link IllegalArgumentException}.
      *
      * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code callback} should be executed.
      * @param callback The callback that should be called when the middleware has information to
      *                 share on the download.
-     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
      * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
      * and some other error code otherwise.
      */
     public int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalDownloadStateCallback internalCallback =
-                new InternalDownloadStateCallback(callback, handler);
+                new InternalDownloadStateCallback(callback, executor);
 
         try {
             int result = downloadService.registerStateCallback(request, internalCallback,
@@ -604,7 +606,7 @@
 
     /**
      * Un-register a callback previously registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}. After
      * this method is called, no further callbacks will be enqueued on the {@link Handler}
      * provided upon registration, even if this method throws an exception.
      *
@@ -692,7 +694,7 @@
      * The state will be delivered as a callback via
      * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
      * callback has been registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}, this
+     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}, this
      * method will be a no-op.
      *
      * If the middleware has no record of the
@@ -775,7 +777,7 @@
      * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
      * obtain another instance of {@link MbmsDownloadSession} immediately after this method
      * returns.
      *
@@ -831,6 +833,36 @@
         }
     }
 
+    private void checkDownloadRequestDestination(DownloadRequest request) {
+        File downloadRequestDestination = new File(request.getDestinationUri().getPath());
+        if (!downloadRequestDestination.isDirectory()) {
+            throw new IllegalArgumentException("The destination path must be a directory");
+        }
+        // Check if the request destination is okay to use by attempting to rename an empty
+        // file to there.
+        File testFile = new File(MbmsTempFileProvider.getEmbmsTempFileDir(mContext),
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+        File testFileDestination = new File(downloadRequestDestination,
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+
+        try {
+            if (!testFile.exists()) {
+                testFile.createNewFile();
+            }
+            if (!testFile.renameTo(testFileDestination)) {
+                throw new IllegalArgumentException("Destination provided in the download request " +
+                        "is invalid -- files in the temp file directory cannot be directly moved " +
+                        "there.");
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException while testing out the destination: "
+                    + e);
+        } finally {
+            testFile.delete();
+            testFileDestination.delete();
+        }
+    }
+
     private File getDownloadRequestTokenPath(DownloadRequest request) {
         File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
                 request.getFileServiceId());
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index fb2ff7b..42c760d4 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -24,9 +24,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.InternalStreamingServiceCallback;
@@ -42,6 +40,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -89,14 +88,11 @@
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
-                    int subscriptionId, Handler handler) {
+    private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+            MbmsStreamingSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+        mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
     }
 
     /**
@@ -117,25 +113,25 @@
      * {@link MbmsStreamingSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The subscription ID to use.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
-     * @param subscriptionId The subscription ID to use.
-     * @param handler The handler you wish to receive callbacks on.
      * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
      */
     public static @Nullable MbmsStreamingSession create(@NonNull Context context,
-            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
-            @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsStreamingSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
         }
-        MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
-                subscriptionId, handler);
+        MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+                subscriptionId, callback);
 
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -148,22 +144,22 @@
 
     /**
      * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+     * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
      */
     public static MbmsStreamingSession create(@NonNull Context context,
-            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsStreamingSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
      * Terminates this instance. Also terminates
      * any streaming services spawned from this instance as if
-     * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+     * {@link StreamingService#close()} had been called on them. After this method returns,
      * no further callbacks originating from the middleware will be enqueued on the provided
      * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
      * obtain another instance of {@link MbmsStreamingSession} immediately after this method
      * returns.
      *
@@ -237,20 +233,20 @@
      * {@link MbmsErrors.StreamingErrors}.
      *
      * @param serviceInfo The information about the service to stream.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback A callback that'll be called when something about the stream changes.
-     * @param handler A handler that calls to {@code callback} should be called on.
      * @return An instance of {@link StreamingService} through which the stream can be controlled.
      *         May be {@code null} if an error occurred.
      */
     public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            StreamingServiceCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, StreamingServiceCallback callback) {
         IMbmsStreamingService streamingService = mService.get();
         if (streamingService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
-                callback, handler);
+                callback, executor);
 
         StreamingService serviceForApp = new StreamingService(
                 mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 30e75b9..d09d426 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -543,9 +543,9 @@
      *                 onSubscriptionsChanged overridden.
      */
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
-        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
-            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+            logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
                     + " listener=" + listener);
         }
         try {
@@ -554,7 +554,7 @@
             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                     "telephony.registry"));
             if (tr != null) {
-                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+                tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
             }
         } catch (RemoteException ex) {
             // Should not happen
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index dfccff4..0c92a68 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3880,6 +3880,9 @@
      * To unregister a listener, pass the listener object and set the
      * events argument to
      * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
      *
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
@@ -4672,8 +4675,6 @@
             return;
         }
 
-        Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
-                " property=" + property + " value: " + value + " propVal=" + propVal);
         SystemProperties.set(property, propVal);
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 7b77491..7d65430 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -393,6 +393,12 @@
      */
     public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
 
+    /**
+     * Call failed because of unobtainable number
+     * @hide
+     */
+    public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
+
     /* OEM specific error codes. To be used by OEMs when they don't want to
    reveal error code which would be replaced by ERROR_UNSPECIFIED */
     public static final int CODE_OEM_CAUSE_1 = 0xf001;
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f0d60b6..602c796 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -27,10 +27,13 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.File;
 import java.io.IOException;
+import java.io.ObjectInput;
 import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
-import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -54,34 +57,116 @@
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
-    private static class OpaqueDataContainer implements Serializable {
-        private final String appIntent;
-        private final int version;
+    private static class SerializationDataContainer implements Externalizable {
+        private String fileServiceId;
+        private Uri source;
+        private Uri destination;
+        private int subscriptionId;
+        private String appIntent;
+        private int version;
 
-        public OpaqueDataContainer(String appIntent, int version) {
-            this.appIntent = appIntent;
-            this.version = version;
+        public SerializationDataContainer() {}
+
+        SerializationDataContainer(DownloadRequest request) {
+            fileServiceId = request.fileServiceId;
+            source = request.sourceUri;
+            destination = request.destinationUri;
+            subscriptionId = request.subscriptionId;
+            appIntent = request.serializedResultIntentForApp;
+            version = request.version;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput objectOutput) throws IOException {
+            objectOutput.write(version);
+            objectOutput.writeUTF(fileServiceId);
+            objectOutput.writeUTF(source.toString());
+            objectOutput.writeUTF(destination.toString());
+            objectOutput.write(subscriptionId);
+            objectOutput.writeUTF(appIntent);
+        }
+
+        @Override
+        public void readExternal(ObjectInput objectInput) throws IOException {
+            version = objectInput.read();
+            fileServiceId = objectInput.readUTF();
+            source = Uri.parse(objectInput.readUTF());
+            destination = Uri.parse(objectInput.readUTF());
+            subscriptionId = objectInput.read();
+            appIntent = objectInput.readUTF();
+            // Do version checks here -- future versions may have other fields.
         }
     }
 
     public static class Builder {
         private String fileServiceId;
         private Uri source;
+        private Uri destination;
         private int subscriptionId;
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+        /**
+         * Constructs a {@link Builder} from a {@link DownloadRequest}
+         * @param other The {@link DownloadRequest} from which the data for the {@link Builder}
+         *              should come.
+         * @return An instance of {@link Builder} pre-populated with data from the provided
+         *         {@link DownloadRequest}.
+         */
+        public static Builder fromDownloadRequest(DownloadRequest other) {
+            Builder result = new Builder(other.sourceUri, other.destinationUri)
+                    .setServiceId(other.fileServiceId)
+                    .setSubscriptionId(other.subscriptionId);
+            result.appIntent = other.serializedResultIntentForApp;
+            // Version of the result is going to be the current version -- as this class gets
+            // updated, new fields will be set to default values in here.
+            return result;
+        }
+
+        /**
+         * This method constructs a new instance of {@link Builder} based on the serialized data
+         * passed in.
+         * @param data A byte array, the contents of which should have been originally obtained
+         *             from {@link DownloadRequest#toByteArray()}.
+         */
+        public static Builder fromSerializedRequest(byte[] data) {
+            Builder builder;
+            try {
+                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+                SerializationDataContainer dataContainer =
+                        (SerializationDataContainer) stream.readObject();
+                builder = new Builder(dataContainer.source, dataContainer.destination);
+                builder.version = dataContainer.version;
+                builder.appIntent = dataContainer.appIntent;
+                builder.fileServiceId = dataContainer.fileServiceId;
+                builder.subscriptionId = dataContainer.subscriptionId;
+            } catch (IOException e) {
+                // Really should never happen
+                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            } catch (ClassNotFoundException e) {
+                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            }
+            return builder;
+        }
 
         /**
          * Builds a new DownloadRequest.
          * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
          *     never be null.
+         * @param destinationUri The final location for the file(s) that are to be downloaded. It
+         *     must be on the same filesystem as the temp file directory set via
+         *     {@link android.telephony.MbmsDownloadSession#setTempFileRootDirectory(File)}.
+         *     The provided path must be a directory that exists. An
+         *     {@link IllegalArgumentException} will be thrown otherwise.
          */
-        public Builder(@NonNull Uri sourceUri) {
-            if (sourceUri == null) {
-                throw new IllegalArgumentException("Source URI must be non-null.");
+        public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri) {
+            if (sourceUri == null || destinationUri == null) {
+                throw new IllegalArgumentException("Source and destination URIs must be non-null.");
             }
             source = sourceUri;
+            destination = destinationUri;
         }
 
         /**
@@ -130,68 +215,34 @@
             return this;
         }
 
-        /**
-         * For use by the middleware to set the byte array of opaque data. The opaque data
-         * includes information about the download request that is used by the client app and the
-         * manager code, but is irrelevant to the middleware.
-         * @param data A byte array, the contents of which should have been originally obtained
-         *             from {@link DownloadRequest#getOpaqueData()}.
-         * @hide
-         */
-        @SystemApi
-        public Builder setOpaqueData(byte[] data) {
-            try {
-                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
-                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
-                version = dataContainer.version;
-                appIntent = dataContainer.appIntent;
-            } catch (IOException e) {
-                // Really should never happen
-                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            } catch (ClassNotFoundException e) {
-                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            }
-            return this;
-        }
-
         public DownloadRequest build() {
-            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
+            return new DownloadRequest(fileServiceId, source, destination,
+                    subscriptionId, appIntent, version);
         }
     }
 
     private final String fileServiceId;
     private final Uri sourceUri;
+    private final Uri destinationUri;
     private final int subscriptionId;
     private final String serializedResultIntentForApp;
     private final int version;
 
     private DownloadRequest(String fileServiceId,
-            Uri source, int sub,
+            Uri source, Uri destination, int sub,
             String appIntent, int version) {
         this.fileServiceId = fileServiceId;
         sourceUri = source;
         subscriptionId = sub;
+        destinationUri = destination;
         serializedResultIntentForApp = appIntent;
         this.version = version;
     }
 
-    public static DownloadRequest copy(DownloadRequest other) {
-        return new DownloadRequest(other);
-    }
-
-    private DownloadRequest(DownloadRequest dr) {
-        fileServiceId = dr.fileServiceId;
-        sourceUri = dr.sourceUri;
-        subscriptionId = dr.subscriptionId;
-        serializedResultIntentForApp = dr.serializedResultIntentForApp;
-        version = dr.version;
-    }
-
     private DownloadRequest(Parcel in) {
         fileServiceId = in.readString();
         sourceUri = in.readParcelable(getClass().getClassLoader());
+        destinationUri = in.readParcelable(getClass().getClassLoader());
         subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         version = in.readInt();
@@ -204,6 +255,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(fileServiceId);
         out.writeParcelable(sourceUri, flags);
+        out.writeParcelable(destinationUri, flags);
         out.writeInt(subscriptionId);
         out.writeString(serializedResultIntentForApp);
         out.writeInt(version);
@@ -224,6 +276,13 @@
     }
 
     /**
+     * @return The destination {@link Uri} of the downloaded file.
+     */
+    public Uri getDestinationUri() {
+        return destinationUri;
+    }
+
+    /**
      * @return The subscription ID on which to perform MBMS operations.
      */
     public int getSubscriptionId() {
@@ -244,19 +303,16 @@
     }
 
     /**
-     * For use by the middleware only. The byte array returned from this method should be
-     * persisted and sent back to the app upon download completion or failure by passing it into
-     * {@link Builder#setOpaqueData(byte[])}.
-     * @return A byte array of opaque data to persist.
-     * @hide
+     * This method returns a byte array that may be persisted to disk and restored to a
+     * {@link DownloadRequest}. The instance of {@link DownloadRequest} persisted by this method
+     * may be recovered via {@link Builder#fromSerializedRequest(byte[])}.
+     * @return A byte array of data to persist.
      */
-    @SystemApi
-    public byte[] getOpaqueData() {
+    public byte[] toByteArray() {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
-            OpaqueDataContainer container = new OpaqueDataContainer(
-                    serializedResultIntentForApp, version);
+            SerializationDataContainer container = new SerializationDataContainer(this);
             stream.writeObject(container);
             stream.flush();
             return byteArrayOutputStream.toByteArray();
@@ -299,15 +355,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isMultipartDownload() {
-        // TODO: figure out what qualifies a request as a multipart download request.
-        return getSourceUri().getLastPathSegment() != null &&
-                getSourceUri().getLastPathSegment().contains("*");
-    }
-
-    /**
      * Retrieves the hash string that should be used as the filename when storing a token for
      * this DownloadRequest.
      * @hide
@@ -320,8 +367,9 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI and the app intent
+            // Hash the source, destination, and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
             if (serializedResultIntentForApp != null) {
                 digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
             }
@@ -344,12 +392,13 @@
                 version == request.version &&
                 Objects.equals(fileServiceId, request.fileServiceId) &&
                 Objects.equals(sourceUri, request.sourceUri) &&
+                Objects.equals(destinationUri, request.destinationUri) &&
                 Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(fileServiceId, sourceUri,
+        return Objects.hash(fileServiceId, sourceUri, destinationUri,
                 subscriptionId, serializedResultIntentForApp, version);
     }
 }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index a7a5958..c2a79d8 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -16,22 +16,23 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsDownloadSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -40,10 +41,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onFileServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onFileServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
index 8702952..f30ae27 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -16,20 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
+import java.util.concurrent.Executor;
+
 /**
  * @hide
  */
 public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final DownloadStateCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
-    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -40,11 +42,16 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
-                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -56,10 +63,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStateUpdated(request, fileInfo, state);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStateUpdated(request, fileInfo, state);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index eb6579ce..e9f39ff 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -16,18 +16,21 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
+import java.util.concurrent.Executor;
+
 /** @hide */
 public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
     private final StreamingServiceCallback mAppCallback;
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private volatile boolean mIsStopped = false;
 
-    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -36,10 +39,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -50,10 +58,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamStateUpdated(state, reason);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamStateUpdated(state, reason);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -64,10 +77,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMediaDescriptionUpdated();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMediaDescriptionUpdated();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -78,10 +96,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -92,10 +115,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamMethodUpdated(methodType);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamMethodUpdated(methodType);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index d782d12..d47f5ad 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -16,21 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsStreamingSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -39,10 +40,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamingServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamingServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9ef188c..b0c00c6 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -21,8 +21,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.MbmsDownloadSession;
@@ -31,14 +33,11 @@
 
 import java.io.File;
 import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -62,6 +61,8 @@
     /** @hide */
     public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
 
+    private static final String EMBMS_INTENT_PERMISSION = "android.permission.SEND_EMBMS_INTENTS";
+
     /**
      * Indicates that the requested operation completed without error.
      * @hide
@@ -137,6 +138,8 @@
     /** @hide */
     @Override
     public void onReceive(Context context, Intent intent) {
+        verifyPermissionIntegrity(context);
+
         if (!verifyIntentContents(context, intent)) {
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
@@ -260,20 +263,18 @@
 
         FileInfo completedFileInfo =
                 (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
-        Path stagingDirectory = FileSystems.getDefault().getPath(
-                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
-                TEMP_FILE_STAGING_LOCATION);
+        Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+                request.getDestinationUri().getPath());
 
-        Uri stagedFileLocation;
+        Uri finalLocation;
         try {
-            stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination);
         } catch (IOException e) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
-                stagedFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
@@ -437,19 +438,22 @@
     }
 
     /*
-     * Moves a tempfile located at fromPath to a new location in the staging directory.
+     * Moves a tempfile located at fromPath to its final home where the app wants it
      */
-    private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
-            Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+            Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+                    " does not have a file scheme");
             return null;
         }
 
         Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
-        if (!Files.isDirectory(stagingDirectory)) {
-            Files.createDirectory(stagingDirectory);
+        if (!Files.isDirectory(appSpecifiedPath)) {
+            Files.createDirectory(appSpecifiedPath);
         }
-        Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
+        // TODO: do we want to support directory trees within the download directory?
+        Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
+                StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
 
         return Uri.fromFile(result.toFile());
     }
@@ -513,39 +517,29 @@
         return mMiddlewarePackageNameCache;
     }
 
-    private static boolean manualMove(File src, File dst) {
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            if (!dst.exists()) {
-                dst.createNewFile();
-            }
-            in = new FileInputStream(src);
-            out = new FileOutputStream(dst);
-            byte[] buffer = new byte[2048];
-            int len;
-            do {
-                len = in.read(buffer);
-                out.write(buffer, 0, len);
-            } while (len > 0);
-        } catch (IOException e) {
-            Log.w(LOG_TAG, "Manual file move failed due to exception "  + e);
-            if (dst.exists()) {
-                dst.delete();
-            }
-            return false;
-        } finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-                if (out != null) {
-                    out.close();
-                }
-            } catch (IOException e) {
-                Log.w(LOG_TAG, "Error closing streams: " + e);
-            }
+    private void verifyPermissionIntegrity(Context context) {
+        PackageManager pm = context.getPackageManager();
+        Intent queryIntent = new Intent(context, MbmsDownloadReceiver.class);
+        List<ResolveInfo> infos = pm.queryBroadcastReceivers(queryIntent, 0);
+        if (infos.size() != 1) {
+            throw new IllegalStateException("Non-unique download receiver in your app");
         }
-        return true;
+        ActivityInfo selfInfo = infos.get(0).activityInfo;
+        if (selfInfo == null) {
+            throw new IllegalStateException("Queried ResolveInfo does not contain a receiver");
+        }
+        if (MbmsUtils.getOverrideServiceName(context,
+                MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION) != null) {
+            // If an override was specified, just make sure that the permission isn't null.
+            if (selfInfo.permission == null) {
+                throw new IllegalStateException(
+                        "MbmsDownloadReceiver must require some permission");
+            }
+            return;
+        }
+        if (!Objects.equals(EMBMS_INTENT_PERMISSION, selfInfo.permission)) {
+            throw new IllegalStateException("MbmsDownloadReceiver must require the " +
+                    "SEND_EMBMS_INTENTS permission.");
+        }
     }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 75ca35e..b5fec44 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -108,8 +108,8 @@
 
         /**
          * Indicates that the app called
-         * {@link MbmsStreamingSession#startStreaming(
-         * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+         * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+         * java.util.concurrent.Executor, StreamingServiceCallback)}
          * more than once for the same {@link StreamingServiceInfo}.
          */
         public static final int ERROR_DUPLICATE_START_STREAM = 303;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 5c130a0..6e03957 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -22,11 +22,12 @@
 import android.telephony.MbmsStreamingSession;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A callback class that is used to receive information from the middleware on MBMS streaming
  * services. An instance of this object should be passed into
- * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+ * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
  */
 public class MbmsStreamingSessionCallback {
     /**
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index b4ad1d7..ef317ee 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -50,7 +50,7 @@
         return new ComponentName(ci.packageName, ci.name);
     }
 
-    private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+    public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
         String metaDataKey = null;
         switch (serviceAction) {
             case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index ec9134a..b6239fe 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -29,11 +29,11 @@
 
 /**
  * Class used to represent a single MBMS stream. After a stream has been started with
- * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)},
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo, java.util.concurrent.Executor,
+ * StreamingServiceCallback)},
  * this class is used to hold information about the stream and control it.
  */
-public class StreamingService {
+public class StreamingService implements AutoCloseable {
     private static final String LOG_TAG = "MbmsStreamingService";
 
     /**
@@ -41,7 +41,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface StreamingState {}
     public final static int STATE_STOPPED = 1;
     public final static int STATE_STARTED = 2;
@@ -53,7 +53,8 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
             REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
             REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
     public @interface StreamingStateChangeReason {}
@@ -64,9 +65,9 @@
     public static final int REASON_NONE = 0;
 
     /**
-     * State changed due to a call to {@link #stopStreaming()} or
+     * State changed due to a call to {@link #close()} or
      * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
-     * StreamingServiceCallback, android.os.Handler)}
+     * java.util.concurrent.Executor, StreamingServiceCallback)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
 
@@ -161,7 +162,8 @@
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
-    public void stopStreaming() {
+    @Override
+    public void close() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
diff --git a/test-base/Android.bp b/test-base/Android.bp
index c008eca..4d149f7 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -24,6 +24,10 @@
 
     srcs: ["src/**/*.java"],
 
+    errorprone: {
+      javacflags: ["-Xep:DepAnn:ERROR"],
+    },
+
     // Needs to be consistent with the repackaged version of this make target.
     java_version: "1.8",
 
diff --git a/test-base/src/android/test/PerformanceTestCase.java b/test-base/src/android/test/PerformanceTestCase.java
index 65bd4a4..2584da2 100644
--- a/test-base/src/android/test/PerformanceTestCase.java
+++ b/test-base/src/android/test/PerformanceTestCase.java
@@ -21,6 +21,11 @@
  *
  * If you want your test to be used as a performance test, you must
  * implement this interface.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
 @Deprecated
 public interface PerformanceTestCase
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 938bb4e..a2edb04 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -23,6 +23,10 @@
     java_version: "1.8",
     srcs: ["src/**/*.java"],
 
+    errorprone: {
+      javacflags: ["-Xep:DepAnn:ERROR"],
+    },
+
     no_framework_libs: true,
     libs: [
         "framework",
diff --git a/test-runner/src/android/test/ComparisonFailure.java b/test-runner/src/android/test/ComparisonFailure.java
index 3fa76f5..d86b700 100644
--- a/test-runner/src/android/test/ComparisonFailure.java
+++ b/test-runner/src/android/test/ComparisonFailure.java
@@ -19,8 +19,9 @@
 /**
  * Thrown when an assert equals for Strings failed.
  * 
- * @deprecated use junit.framework.ComparisonFailure
+ * @deprecated use org.junit.ComparisonFailure
  */
+@Deprecated
 public class ComparisonFailure extends AssertionFailedError {
     private junit.framework.ComparisonFailure mComparison;
 
diff --git a/test-runner/src/android/test/TestSuiteProvider.java b/test-runner/src/android/test/TestSuiteProvider.java
index c74651c..12cfcb7 100644
--- a/test-runner/src/android/test/TestSuiteProvider.java
+++ b/test-runner/src/android/test/TestSuiteProvider.java
@@ -20,6 +20,11 @@
 
 /**
  * Implementors will know how to get a test suite.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
 @Deprecated
 public interface TestSuiteProvider {
diff --git a/test-runner/src/junit/runner/BaseTestRunner.java b/test-runner/src/junit/runner/BaseTestRunner.java
index e7e0431..b2fa16c 100644
--- a/test-runner/src/junit/runner/BaseTestRunner.java
+++ b/test-runner/src/junit/runner/BaseTestRunner.java
@@ -207,6 +207,7 @@
 	 *
 	 * @deprecated not present in JUnit4.10
 	 */
+        @Deprecated
 	public TestSuiteLoader getLoader() {
 		return new StandardTestSuiteLoader();
 	}
@@ -279,6 +280,7 @@
 
 	// BEGIN android-changed - add back this method for API compatibility
 	/** @deprecated not present in JUnit4.10 */
+        @Deprecated
 	public static boolean inVAJava() {
 		return false;
 	}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 24639e9..28f8122 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -50,6 +50,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 
 import static com.android.internal.util.TestUtils.waitForIdleHandler;
+import static com.android.internal.util.TestUtils.waitForIdleLooper;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -85,6 +86,7 @@
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
+import android.net.ConnectivityThread;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.IpPrefix;
@@ -279,6 +281,7 @@
         waitForIdle(mWiFiNetworkAgent, timeoutMs);
         waitForIdle(mEthernetNetworkAgent, timeoutMs);
         waitForIdleHandler(mService.mHandlerThread, timeoutMs);
+        waitForIdleLooper(ConnectivityThread.getInstanceLooper(), timeoutMs);
     }
 
     public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
@@ -1438,9 +1441,9 @@
                 expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
             }
             if (expectValidated) {
-                expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+                expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
             } else {
-                expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent);
+                expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
             }
             expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
         }
@@ -1479,14 +1482,24 @@
         }
 
         NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
-            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+            return expectCapabilitiesWith(capability, agent, TIMEOUT_MS);
+        }
+
+        NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
+                int timeoutMs) {
+            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertTrue(nc.hasCapability(capability));
             return nc;
         }
 
         NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
-            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+            return expectCapabilitiesWithout(capability, agent, TIMEOUT_MS);
+        }
+
+        NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
+                int timeoutMs) {
+            CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertFalse(nc.hasCapability(capability));
             return nc;
@@ -1831,6 +1844,51 @@
     }
 
     @Test
+    public void testNetworkGoesIntoBackgroundAfterLinger() {
+        setMobileDataAlwaysOn(true);
+        NetworkRequest request = new NetworkRequest.Builder()
+                .clearCapabilities()
+                .build();
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+
+        TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        // Wifi comes up and cell lingers.
+        mWiFiNetworkAgent.connect(true);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+        // File a request for cellular, then release it.
+        NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        NetworkCallback noopCallback = new NetworkCallback();
+        mCm.requestNetwork(cellRequest, noopCallback);
+        mCm.unregisterNetworkCallback(noopCallback);
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+        // Let linger run its course.
+        callback.assertNoCallback();
+        final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+        callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent,
+                lingerTimeoutMs);
+
+        // Clean up.
+        mCm.unregisterNetworkCallback(defaultCallback);
+        mCm.unregisterNetworkCallback(callback);
+    }
+
+    @Test
     public void testExplicitlySelected() {
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -3125,6 +3183,9 @@
         InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
         InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
 
+        final int validKaInterval = 15;
+        final int invalidKaInterval = 9;
+
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("wlan12");
         lp.addLinkAddress(new LinkAddress(myIPv6, 64));
@@ -3139,36 +3200,37 @@
         PacketKeepalive ka;
 
         // Attempt to start keepalives with invalid parameters and check for errors.
-        ka = mCm.startNattKeepalive(notMyNet, 25, callback, myIPv4, 1234, dstIPv4);
+        ka = mCm.startNattKeepalive(notMyNet, validKaInterval, callback, myIPv4, 1234, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
 
-        ka = mCm.startNattKeepalive(myNet, 19, callback, notMyIPv4, 1234, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, invalidKaInterval, callback, myIPv4, 1234, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 1234, dstIPv6);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 1234, dstIPv6);
         callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv6);
-        callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);  // NAT-T is IPv4-only.
+        // NAT-T is only supported for IPv4.
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6);
+        callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
 
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
 
         // Check that a started keepalive can be stopped.
         mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
         mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
         ka.stop();
@@ -3176,7 +3238,7 @@
 
         // Check that deleting the IP address stops the keepalive.
         LinkProperties bogusLp = new LinkProperties(lp);
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
         bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
         bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
@@ -3185,7 +3247,7 @@
         mWiFiNetworkAgent.sendLinkProperties(lp);
 
         // Check that a started keepalive is stopped correctly when the network disconnects.
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
         mWiFiNetworkAgent.disconnect();
         waitFor(mWiFiNetworkAgent.getDisconnectedCV());
@@ -3202,7 +3264,7 @@
         mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
 
         // Check things work as expected when the keepalive is stopped and the network disconnects.
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
         ka.stop();
         mWiFiNetworkAgent.disconnect();
@@ -3216,13 +3278,14 @@
 
         // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
-        ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+        ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
         callback.expectStarted();
 
         // The second one gets slot 2.
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
         TestKeepaliveCallback callback2 = new TestKeepaliveCallback();
-        PacketKeepalive ka2 = mCm.startNattKeepalive(myNet, 25, callback2, myIPv4, 6789, dstIPv4);
+        PacketKeepalive ka2 = mCm.startNattKeepalive(
+                myNet, validKaInterval, callback2, myIPv4, 6789, dstIPv4);
         callback2.expectStarted();
 
         // Now stop the first one and create a third. This also gets slot 1.
@@ -3231,7 +3294,8 @@
 
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
         TestKeepaliveCallback callback3 = new TestKeepaliveCallback();
-        PacketKeepalive ka3 = mCm.startNattKeepalive(myNet, 25, callback3, myIPv4, 9876, dstIPv4);
+        PacketKeepalive ka3 = mCm.startNattKeepalive(
+                myNet, validKaInterval, callback3, myIPv4, 9876, dstIPv4);
         callback3.expectStarted();
 
         ka2.stop();