Merge "AssetManager2: Fix issue with native cast"
diff --git a/api/current.txt b/api/current.txt
index 9204289..3944b45 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -73,6 +73,7 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
+    field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a6d667..3dbb333 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -829,6 +829,7 @@
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
     field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
     field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
     field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6bca16f..4c6a36b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -24,6 +24,7 @@
 import "frameworks/base/core/proto/android/app/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
 
@@ -98,6 +99,7 @@
         DaveyOccurred davey_occurred = 58;
         OverlayStateChanged overlay_state_changed = 59;
         ForegroundServiceStateChanged foreground_service_state_changed = 60;
+        CallStateChanged call_state_changed = 61;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -725,6 +727,33 @@
     optional int64 time_since_last_boot = 6;
 }
 
+
+/**
+ * Logs call state and disconnect cause (if applicable).
+ *
+ * Logged from:
+ *   packages/services/Telecomm/src/com/android/server/telecom/Call.java
+ */
+message CallStateChanged {
+    // The state of the call. Eg. DIALING, ACTIVE, ON_HOLD, DISCONNECTED.
+    // From frameworks/base/core/proto/android/telecomm/enums.proto.
+    optional android.telecom.CallStateEnum call_state = 1;
+
+    // The reason the call disconnected. Eg. ERROR, MISSED, REJECTED, BUSY.
+    // This value is only applicable when the call_state is DISCONNECTED, and
+    // should always be UNKNOWN if the call_state is not DISCONNECTED.
+    // From frameworks/base/core/proto/android/telecomm/enums.proto.
+    optional android.telecom.DisconnectCauseEnum disconnect_cause = 2;
+
+    // True if the call is self-managed, which are apps that use the
+    // telecom infrastructure to make their own calls.
+    optional bool self_managed = 3;
+
+    // True if call is external. External calls are calls on connected Wear
+    // devices but show up in Telecom so the user can pull them onto the device.
+    optional bool external_call = 4;
+}
+
 /**
  * Logs the duration of a davey (jank of >=700ms) when it occurs
  *
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 256c479..ea0fd75 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -471,14 +471,6 @@
      * {@link #onStart} and returns either {@link #START_STICKY}
      * or {@link #START_STICKY_COMPATIBILITY}.
      * 
-     * <p>If you need your application to run on platform versions prior to API
-     * level 5, you can use the following model to handle the older {@link #onStart}
-     * callback in that case.  The <code>handleCommand</code> method is implemented by
-     * you as appropriate:
-     * 
-     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
-     *   start_compatibility}
-     *
      * <p class="caution">Note that the system calls this on your
      * service's main thread.  A service's main thread is the same
      * thread where UI operations take place for Activities running in the
@@ -687,6 +679,10 @@
      * {@link #startService(Intent)} first to tell the system it should keep the service running,
      * and then use this method to tell it to keep it running harder.</p>
      *
+     * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request
+     * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
+     * this API.</p>
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fa73e3c..12d4079 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2399,6 +2399,26 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+    /**
+     * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+     * such that the device may be eligible for the installation of additional configuration splits.
+     * Configuration properties that can trigger this broadcast include locale and display density.
+     *
+     * <p class="note">
+     * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+     * components declared in manifests. However, the receiver <em>must</em> hold the
+     * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+            "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
     /**
      * Broadcast Action: The current device's locale has changed.
      *
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a817f33..017674f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1818,9 +1818,9 @@
     }
 
     /**
-     * Called when the input method window has been shown to the user, after
-     * previously not being visible.  This is done after all of the UI setup
-     * for the window has occurred (creating its views etc).
+     * Called immediately before the input method window is shown to the user.
+     * You could override this to prepare for the window to be shown
+     * (update view structure etc).
      */
     public void onWindowShown() {
         // Intentionally empty
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 48f5684..fc78861 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -894,6 +894,14 @@
 
         /**
          * P.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>{@link android.app.Service#startForeground Service.startForeground} requires
+         * that apps hold the permission
+         * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+         * </ul>
          */
         public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
     }
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c623ca6..2334e03 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -403,38 +403,30 @@
     return nullptr;
   }
 
-  std::vector<std::string> all_file_paths;
-  {
-    StringPiece normalized_path = path_utf8.c_str();
-    if (normalized_path.data()[0] == '/') {
-      normalized_path = normalized_path.substr(1);
-    }
-    std::string root_path = StringPrintf("assets/%s", normalized_path.data());
-    ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-    for (const ApkAssets* assets : assetmanager->GetApkAssets()) {
-      assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) {
-        if (type == FileType::kFileTypeRegular) {
-          all_file_paths.push_back(file_path.to_string());
-        }
-      });
-    }
+  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+  std::unique_ptr<AssetDir> asset_dir =
+      assetmanager->OpenDir(path_utf8.c_str());
+  if (asset_dir == nullptr) {
+    jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
+    return nullptr;
   }
 
-  jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr);
+  const size_t file_count = asset_dir->getFileCount();
+
+  jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr);
   if (array == nullptr) {
     return nullptr;
   }
 
-  jsize index = 0;
-  for (const std::string& file_path : all_file_paths) {
-    jstring java_string = env->NewStringUTF(file_path.c_str());
+  for (size_t i = 0; i < file_count; i++) {
+    jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
 
     // Check for errors creating the strings (if malformed or no memory).
     if (env->ExceptionCheck()) {
      return nullptr;
     }
 
-    env->SetObjectArrayElement(array, index++, java_string);
+    env->SetObjectArrayElement(array, i, java_string);
 
     // If we have a large amount of string in our array, we might overflow the
     // local reference table of the VM.
diff --git a/core/proto/android/telecomm/enums.proto b/core/proto/android/telecomm/enums.proto
new file mode 100644
index 0000000..7a2ba62
--- /dev/null
+++ b/core/proto/android/telecomm/enums.proto
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.telecom;
+
+option java_outer_classname = "TelecomProtoEnums";
+option java_multiple_files = true;
+
+/**
+ * Call states, primarily used in CallState.java,
+ * Call.java, and CallsManager.java in packages/services.
+ */
+enum CallStateEnum {
+    /**
+     * Indicates that a call is new and not connected. This is used as the default state internally
+     * within Telecom and should not be used between Telecom and call services. Call services are
+     * not expected to ever interact with NEW calls, but {@link android.telecom.InCallService}s will
+     * see calls in this state.
+     */
+    NEW = 0;
+
+    /**
+     * The initial state of an outgoing {@code Call}.
+     * Common transitions are to {@link #DIALING} state for a successful call or
+     * {@link #DISCONNECTED} if it failed.
+     */
+    CONNECTING = 1;
+
+    /**
+     * The state of an outgoing {@code Call} when waiting on user to select a
+     * {@link android.telecom.PhoneAccount} through which to place the call.
+     */
+    SELECT_PHONE_ACCOUNT = 2;
+
+    /**
+     * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
+     * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
+     * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
+     * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
+     */
+    DIALING = 3;
+
+    /**
+     * Indicates that a call is incoming and the user still has the option of answering, rejecting,
+     * or doing nothing with the call. This state is usually associated with some type of audible
+     * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
+     * otherwise.
+     */
+    RINGING = 4;
+
+    /**
+     * Indicates that a call is currently connected to another party and a communication channel is
+     * open between them. The normal transition to this state is by the user answering a
+     * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
+     */
+    ACTIVE = 5;
+
+    /**
+     * Indicates that the call is currently on hold. In this state, the call is not terminated
+     * but no communication is allowed until the call is no longer on hold. The typical transition
+     * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
+     * an action, such as clicking the hold button.
+     */
+    ON_HOLD = 6;
+
+    /**
+     * Indicates that a call is currently disconnected. All states can transition to this state
+     * by the call service giving notice that the connection has been severed. When the user
+     * explicitly ends a call, it will not transition to this state until the call service confirms
+     * the disconnection or communication was lost to the call service currently responsible for
+     * this call (e.g., call service crashes).
+     */
+    DISCONNECTED = 7;
+
+    /**
+     * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
+     * time of writing) but cancelled before it was successfully connected.
+     */
+    ABORTED = 8;
+
+    /**
+     * Indicates that the call is in the process of being disconnected and will transition next
+     * to a {@link #DISCONNECTED} state.
+     * <p>
+     * This state is not expected to be communicated from the Telephony layer, but will be reported
+     * to the InCall UI for calls where disconnection has been initiated by the user but the
+     * ConnectionService has confirmed the call as disconnected.
+     */
+    DISCONNECTING = 9;
+
+    /**
+     * Indicates that the call is in the process of being pulled to the local device.
+     * <p>
+     * This state should only be set on a call with
+     * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and
+     * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}.
+     */
+    PULLING = 10;
+}
+
+// Disconnect causes for a call. Primarily used by android/telecom/DisconnectCause.java
+enum DisconnectCauseEnum {
+    /**
+     * Disconnected because of an unknown or unspecified reason.
+     */
+    UNKNOWN = 0;
+
+    /**
+     * Disconnected because there was an error, such as a problem with the network.
+     */
+    ERROR = 1;
+
+    /**
+     * Disconnected because of a local user-initiated action, such as hanging up.
+     */
+    LOCAL = 2;
+
+    /**
+     * Disconnected because of a remote user-initiated action, such as the other party hanging up
+     * up.
+     */
+    REMOTE = 3;
+
+    /**
+     * Disconnected because it has been canceled.
+     */
+    CANCELED = 4;
+
+    /**
+     * Disconnected because there was no response to an incoming call.
+     */
+    MISSED = 5;
+
+    /**
+     * Disconnected because the user rejected an incoming call.
+     */
+    REJECTED = 6;
+
+    /**
+     * Disconnected because the other party was busy.
+     */
+    BUSY = 7;
+
+    /**
+     * Disconnected because of a restriction on placing the call, such as dialing in airplane
+     * mode.
+     */
+    RESTRICTED = 8;
+
+    /**
+     * Disconnected for reason not described by other disconnect codes.
+     */
+    OTHER = 9;
+
+    /**
+     * Disconnected because the connection manager did not support the call. The call will be tried
+     * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
+     */
+    CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+
+    /**
+     * Disconnected because the user did not locally answer the incoming call, but it was answered
+     * on another device where the call was ringing.
+     */
+    ANSWERED_ELSEWHERE = 11;
+
+    /**
+     * Disconnected because the call was pulled from the current device to another device.
+     */
+    CALL_PULLED = 12;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index deefddb..f6f1d81 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -53,6 +53,7 @@
     <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
     <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
     <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.SPLIT_CONFIGURATION_CHANGED" />
     <protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
     <protected-broadcast android:name="android.intent.action.BATTERY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.BATTERY_LOW" />
@@ -3784,6 +3785,15 @@
     <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
         android:protectionLevel="signature|development|instant|appop" />
 
+    <!-- Allows a regular application to use {@link android.app.Service#startForeground
+         Service.startForeground}.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.FOREGROUND_SERVICE"
+        android:description="@string/permdesc_foregroundService"
+        android:label="@string/permlab_foregroundService"
+        android:protectionLevel="normal|instant" />
+
     <!-- @hide Allows system components to access all app shortcuts. -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
         android:protectionLevel="signature" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec81df7..2b7b056 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -916,6 +916,11 @@
     <string name="permdesc_persistentActivity" product="default">Allows the app to make parts of itself persistent in memory.  This can limit memory available to other apps slowing down the phone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_foregroundService">run foreground service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_getPackageSize">measure app storage space</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 7d5c60a..53c22f6 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -51,6 +51,7 @@
     <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index da0205d..60f8a18 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -231,12 +231,16 @@
   while ((result = ::Next(cookie, &entry, &name)) == 0) {
     StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
     StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-    auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
-    if (iter != leaf_file_path.end()) {
-      dirs.insert(
-          leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
-    } else if (!leaf_file_path.empty()) {
-      f(leaf_file_path, kFileTypeRegular);
+
+    if (!leaf_file_path.empty()) {
+      auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+      if (iter != leaf_file_path.end()) {
+        std::string dir =
+            leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+        dirs.insert(std::move(dir));
+      } else {
+        f(leaf_file_path, kFileTypeRegular);
+      }
     }
   }
   ::EndIteration(cookie);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index eaf79cb..7cac2b3 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -36,6 +36,10 @@
 namespace lib_two = com::android::lib_two;
 namespace libclient = com::android::libclient;
 
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::StrEq;
+
 namespace android {
 
 class AssetManager2Test : public ::testing::Test {
@@ -64,6 +68,9 @@
 
     system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
     ASSERT_NE(nullptr, system_assets_);
+
+    app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
+    ASSERT_THAT(app_assets_, NotNull());
   }
 
  protected:
@@ -75,6 +82,7 @@
   std::unique_ptr<const ApkAssets> libclient_assets_;
   std::unique_ptr<const ApkAssets> appaslib_assets_;
   std::unique_ptr<const ApkAssets> system_assets_;
+  std::unique_ptr<const ApkAssets> app_assets_;
 };
 
 TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -465,8 +473,68 @@
             assetmanager.GetResourceId("main", "layout", "com.android.basic"));
 }
 
-TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get()});
 
-TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+  std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+  ASSERT_THAT(asset, NotNull());
+
+  const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+  ASSERT_THAT(data, NotNull());
+  EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n"));
+}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+  std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
+  ASSERT_THAT(asset, NotNull());
+
+  const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
+  ASSERT_THAT(data, NotNull());
+  EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n"));
+}
+
+TEST_F(AssetManager2Test, OpenDir) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get()});
+
+  std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(2u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir")));
+  EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory));
+
+  asset_dir = assetmanager.OpenDir("subdir");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(1u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+}
+
+TEST_F(AssetManager2Test, OpenDirFromManyApks) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
+
+  std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
+  ASSERT_THAT(asset_dir, NotNull());
+  ASSERT_THAT(asset_dir->getFileCount(), Eq(3u));
+
+  EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt")));
+  EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular));
+
+  EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir")));
+  EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
+}
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk
index ccb0824..c8ad86d 100644
--- a/libs/androidfw/tests/data/app/app.apk
+++ b/libs/androidfw/tests/data/app/app.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt
new file mode 100644
index 0000000..b214e06
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/app_file.txt
@@ -0,0 +1 @@
+app file
diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt
new file mode 100644
index 0000000..0811542
--- /dev/null
+++ b/libs/androidfw/tests/data/app/assets/file.txt
@@ -0,0 +1 @@
+app override file
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
index d418158..09af842 100755
--- a/libs/androidfw/tests/data/app/build
+++ b/libs/androidfw/tests/data/app/build
@@ -17,4 +17,11 @@
 
 set -e
 
-aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link \
+    --manifest AndroidManifest.xml \
+    -I ../system/system.apk \
+    -A assets \
+    -o app.apk \
+    compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/system/assets/file.txt b/libs/androidfw/tests/data/system/assets/file.txt
new file mode 100644
index 0000000..f73f309
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/file.txt
@@ -0,0 +1 @@
+file
diff --git a/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
new file mode 100644
index 0000000..3f74eb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt
@@ -0,0 +1 @@
+subdir file
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
index bfbdf4c..b65145a 100755
--- a/libs/androidfw/tests/data/system/build
+++ b/libs/androidfw/tests/data/system/build
@@ -17,4 +17,6 @@
 
 set -e
 
-aapt package -x -M AndroidManifest.xml -S res -F system.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -A assets -o system.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
index b97bdb6..5f60d21 100644
--- a/libs/androidfw/tests/data/system/res/values-sv/values.xml
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -15,6 +15,5 @@
 -->
 
 <resources>
-    <public type="integer" name="number" id="0x01030000" />
     <integer name="number">1</integer>
 </resources>
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
index 35d43c7..7893c94 100644
--- a/libs/androidfw/tests/data/system/res/values/themes.xml
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -18,6 +18,7 @@
     <public name="background" type="attr" id="0x01010000"/>
     <public name="foreground" type="attr" id="0x01010001"/>
     <public name="Theme.One" type="style" id="0x01020000"/>
+    <public type="integer" name="number" id="0x01030000" />
 
     <attr name="background" format="color|reference"/>
     <attr name="foreground" format="color|reference"/>
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 1299016..9045d6c 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ
diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml
index 8d79f62..c0a59b3 100644
--- a/packages/MtpDocumentsProvider/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/AndroidManifest.xml
@@ -3,6 +3,7 @@
           package="com.android.mtp"
           android:sharedUserId="android.media">
     <uses-feature android:name="android.hardware.usb.host" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 64b2ae6..79299aa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -44,6 +44,7 @@
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.USE_RESERVED_DISK" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <!-- System tool permissions granted to the shell. -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 14404f5..230f69d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1043,20 +1043,14 @@
                         throw new SecurityException("Instant app " + r.appInfo.packageName
                                 + " does not have permission to create foreground services");
                     default:
-                        try {
-                            if (AppGlobals.getPackageManager().checkPermission(
-                                    android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
-                                    r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
-                                            != PackageManager.PERMISSION_GRANTED) {
-                                throw new SecurityException("Instant app " + r.appInfo.packageName
-                                        + " does not have permission to create foreground"
-                                        + "services");
-                            }
-                        } catch (RemoteException e) {
-                            throw new SecurityException("Failed to check instant app permission." ,
-                                    e);
-                        }
+                        mAm.enforcePermission(
+                                android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
+                                r.app.pid, r.appInfo.uid, "startForeground");
                 }
+            } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
+                mAm.enforcePermission(
+                        android.Manifest.permission.FOREGROUND_SERVICE,
+                        r.app.pid, r.appInfo.uid, "startForeground");
             }
             if (r.fgRequired) {
                 if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2aa5589..e44223b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8889,6 +8889,20 @@
     /**
      * This can be called with or without the global lock held.
      */
+    void enforcePermission(String permission, int pid, int uid, String func) {
+        if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+
+        String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid
+                + " requires " + permission;
+        Slog.w(TAG, msg);
+        throw new SecurityException(msg);
+    }
+
+    /**
+     * This can be called with or without the global lock held.
+     */
     void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
         if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
             enforceCallingPermission(permission, func);
@@ -21934,7 +21948,7 @@
         }
         try {
             if (values != null) {
-                changes = updateGlobalConfiguration(values, initLocale, persistent, userId,
+                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
                         deferResume);
             }
 
@@ -21952,8 +21966,16 @@
         return kept;
     }
 
+    /**
+     * Returns true if this configuration change is interesting enough to send an
+     * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+     */
+    private static boolean isSplitConfigurationChange(int configDiff) {
+        return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+    }
+
     /** Update default (global) configuration and notify listeners about changes. */
-    private int updateGlobalConfiguration(@NonNull Configuration values, boolean initLocale,
+    private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
             boolean persistent, int userId, boolean deferResume) {
         mTempConfig.setTo(getGlobalConfiguration());
         final int changes = mTempConfig.updateFrom(values);
@@ -22056,6 +22078,19 @@
                     UserHandle.USER_ALL);
         }
 
+        // Send a broadcast to PackageInstallers if the configuration change is interesting
+        // for the purposes of installing additional splits.
+        if (!initLocale && isSplitConfigurationChange(changes)) {
+            intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+            // Typically only app stores will have this permission.
+            String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
+                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+        }
+
         // Override configuration of the default display duplicates global config, so we need to
         // update it also. This will also notify WindowManager about changes.
         performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
@@ -22128,7 +22163,7 @@
                     // Override configuration of the default display duplicates global config, so
                     // we're calling global config update instead for default display. It will also
                     // apply the correct override config.
-                    changes = updateGlobalConfiguration(values, false /* initLocale */,
+                    changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
                             false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
                 } else {
                     changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index e103464..cd9311f 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -26,6 +26,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -40,7 +42,9 @@
 import android.app.IActivityManager;
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
 import android.app.backup.FullBackupDataOutput;
 import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
@@ -53,6 +57,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -80,6 +85,8 @@
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
@@ -88,6 +95,7 @@
 import org.robolectric.shadows.ShadowQueuedWork;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Stream;
@@ -184,32 +192,33 @@
 
     @Test
     public void testRunTask_whenTransportProvidesFlags_passesThemToTheAgent() throws Exception {
-        BackupAgent agent = setUpAgent(PACKAGE_1);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
         int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
         when(mTransportBinder.getTransportFlags()).thenReturn(flags);
         PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
 
         runTask(task);
 
-        verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+        verify(agentMock.agent)
+                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
     }
 
     @Test
     public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
-        BackupAgent agent = setUpAgent(PACKAGE_1);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
         PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
 
         runTask(task);
 
-        verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
+        verify(agentMock.agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
     }
 
     @Test
     public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
             throws Exception {
-        List<BackupAgent> agents = setUpAgents(PACKAGE_1, PACKAGE_2);
-        BackupAgent agent1 = agents.get(0);
-        BackupAgent agent2 = agents.get(1);
+        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
+        BackupAgent agent1 = agentMocks.get(0).agent;
+        BackupAgent agent2 = agentMocks.get(1).agent;
         int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
         when(mTransportBinder.getTransportFlags()).thenReturn(flags);
         PerformBackupTask task =
@@ -223,14 +232,103 @@
 
     @Test
     public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
-        BackupAgent agent = setUpAgent(PACKAGE_1);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
         PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
         int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
         when(mTransportBinder.getTransportFlags()).thenReturn(flags);
 
         runTask(task);
 
-        verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+        verify(agentMock.agent)
+                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+    }
+
+    @Test
+    public void testRunTask_callsListenerOnTaskFinished() throws Exception {
+        setUpAgent(PACKAGE_1);
+        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+    }
+
+    @Test
+    public void testRunTask_callsTransportPerformBackup() throws Exception {
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock.agent,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key1", "foo".getBytes());
+                    writeData(dataOutput, "key2", "bar".getBytes());
+                });
+        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+        // We need to verify at call time because the file is deleted right after
+        when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .then(this::mockAndVerifyTransportPerformBackupData);
+
+        runTask(task);
+
+        // Already verified data in mockAndVerifyPerformBackupData
+        verify(mTransportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+    }
+
+    private int mockAndVerifyTransportPerformBackupData(InvocationOnMock invocation)
+            throws IOException {
+        ParcelFileDescriptor data = invocation.getArgument(1);
+
+        // Verifying that what we passed to the transport is what the agent wrote
+        BackupDataInput dataInput = new BackupDataInput(data.getFileDescriptor());
+
+        // "key1" => "foo"
+        assertThat(dataInput.readNextHeader()).isTrue();
+        assertThat(dataInput.getKey()).isEqualTo("key1");
+        int size1 = dataInput.getDataSize();
+        byte[] data1 = new byte[size1];
+        dataInput.readEntityData(data1, 0, size1);
+        assertThat(data1).isEqualTo("foo".getBytes());
+
+        // "key2" => "bar"
+        assertThat(dataInput.readNextHeader()).isTrue();
+        assertThat(dataInput.getKey()).isEqualTo("key2");
+        int size2 = dataInput.getDataSize();
+        byte[] data2 = new byte[size2];
+        dataInput.readEntityData(data2, 0, size2);
+        assertThat(data2).isEqualTo("bar".getBytes());
+
+        // No more
+        assertThat(dataInput.readNextHeader()).isFalse();
+
+        return BackupTransport.TRANSPORT_OK;
+    }
+
+    @Test
+    public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
+            throws Exception {
+        setUpAgent(PACKAGE_1);
+        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+        when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_OK);
+
+        runTask(task);
+
+        verify(mTransportBinder).finishBackup();
+    }
+
+    @Test
+    public void testRunTask_whenProhibitedKey_failsAgent() throws Exception {
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock.agent,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
+                });
+        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agentBinder).fail(any());
     }
 
     private void runTask(PerformBackupTask task) {
@@ -241,26 +339,34 @@
         }
     }
 
-    private List<BackupAgent> setUpAgents(String... packageNames) {
+    private List<AgentMock> setUpAgents(String... packageNames) {
         return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
     }
 
-    private BackupAgent setUpAgent(String packageName) {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = packageName;
-        packageInfo.applicationInfo = new ApplicationInfo();
-        packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_ALLOW_BACKUP;
-        packageInfo.applicationInfo.backupAgentName = "BackupAgent" + packageName;
-        packageInfo.applicationInfo.packageName = packageName;
-        mShadowPackageManager.setApplicationEnabledSetting(
-                packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
-        mShadowPackageManager.addPackage(packageInfo);
-        BackupAgent backupAgent = spy(BackupAgent.class);
-        IBackupAgent backupAgentBinder = IBackupAgent.Stub.asInterface(backupAgent.onBind());
-        when(mBackupManagerService.bindToAgentSynchronous(
-                        eq(packageInfo.applicationInfo), anyInt()))
-                .thenReturn(backupAgentBinder);
-        return backupAgent;
+    private AgentMock setUpAgent(String packageName) {
+        try {
+            PackageInfo packageInfo = new PackageInfo();
+            packageInfo.packageName = packageName;
+            packageInfo.applicationInfo = new ApplicationInfo();
+            packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_ALLOW_BACKUP;
+            packageInfo.applicationInfo.backupAgentName = "BackupAgent" + packageName;
+            packageInfo.applicationInfo.packageName = packageName;
+            mShadowPackageManager.setApplicationEnabledSetting(
+                    packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+            mShadowPackageManager.addPackage(packageInfo);
+            BackupAgent backupAgent = spy(BackupAgent.class);
+            IBackupAgent backupAgentBinder =
+                    spy(IBackupAgent.Stub.asInterface(backupAgent.onBind()));
+            // Don't crash our only process (in production code this would crash the app, not us)
+            doNothing().when(backupAgentBinder).fail(any());
+            when(mBackupManagerService.bindToAgentSynchronous(
+                            eq(packageInfo.applicationInfo), anyInt()))
+                    .thenReturn(backupAgentBinder);
+            return new AgentMock(backupAgentBinder, backupAgent);
+        } catch (RemoteException e) {
+            // Never happens, compiler happy
+            throw new AssertionError(e);
+        }
     }
 
     private PerformBackupTask createPerformBackupTask(
@@ -288,10 +394,53 @@
         return task;
     }
 
-    private ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
+    private static ArgumentMatcher<PackageInfo> packageInfo(String packageName) {
+        return packageInfo -> packageName.equals(packageInfo.packageName);
+    }
+
+    private static ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
         return dataOutput -> dataOutput.getTransportFlags() == flags;
     }
 
+    private static void writeData(BackupDataOutput dataOutput, String key, byte[] data)
+            throws IOException {
+        dataOutput.writeEntityHeader(key, data.length);
+        dataOutput.writeEntityData(data, data.length);
+    }
+
+    private static void agentOnBackupDo(BackupAgent agent, BackupAgentOnBackup function)
+            throws Exception {
+        doAnswer(function).when(agent).onBackup(any(), any(), any());
+    }
+
+    @FunctionalInterface
+    private interface BackupAgentOnBackup extends Answer<Void> {
+        void onBackup(
+                ParcelFileDescriptor oldState,
+                BackupDataOutput dataOutput,
+                ParcelFileDescriptor newState)
+                throws IOException;
+
+        @Override
+        default Void answer(InvocationOnMock invocation) throws Throwable {
+            onBackup(
+                    invocation.getArgument(0),
+                    invocation.getArgument(1),
+                    invocation.getArgument(2));
+            return null;
+        }
+    }
+
+    private static class AgentMock {
+        private final IBackupAgent agentBinder;
+        private final BackupAgent agent;
+
+        public AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
+            this.agentBinder = agentBinder;
+            this.agent = agent;
+        }
+    }
+
     private abstract static class FakeIBackupManager extends IBackupManager.Stub {
         private Handler mBackupHandler;
         private BackupRestoreTask mTask;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
index 28489af..8016a8b 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
@@ -21,41 +21,76 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+import java.io.EOFException;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
 
+/**
+ * Shadow for {@link BackupDataInput}. Format read does NOT match implementation. To write data to
+ * be read by this shadow, you should also declare shadow {@link ShadowBackupDataOutput}.
+ */
 @Implements(BackupDataInput.class)
 public class ShadowBackupDataInput {
-    @Implementation
-    public void __constructor__(FileDescriptor fd) {
-    }
+    private ObjectInputStream mInput;
+    private int mSize;
+    private String mKey;
+    private boolean mHeaderReady;
 
     @Implementation
-    protected void finalize() throws Throwable {
+    public void __constructor__(FileDescriptor fd) {
+        try {
+            mInput = new ObjectInputStream(new FileInputStream(fd));
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
     }
 
     @Implementation
     public boolean readNextHeader() throws IOException {
-        return false;
+        mHeaderReady = false;
+        try {
+            mSize = mInput.readInt();
+        } catch (EOFException e) {
+            return false;
+        }
+        mKey = mInput.readUTF();
+        mHeaderReady = true;
+        return true;
     }
 
     @Implementation
     public String getKey() {
-        throw new AssertionError("Can't call because readNextHeader() returned false");
+        checkHeaderReady();
+        return mKey;
     }
 
     @Implementation
     public int getDataSize() {
-        throw new AssertionError("Can't call because readNextHeader() returned false");
+        checkHeaderReady();
+        return mSize;
     }
 
     @Implementation
     public int readEntityData(byte[] data, int offset, int size) throws IOException {
-        throw new AssertionError("Can't call because readNextHeader() returned false");
+        checkHeaderReady();
+        int result = mInput.read(data, offset, size);
+        if (result < 0) {
+            throw new IOException("result=0x" + Integer.toHexString(result));
+        }
+        return result;
     }
 
     @Implementation
     public void skipEntityData() throws IOException {
-        throw new AssertionError("Can't call because readNextHeader() returned false");
+        checkHeaderReady();
+        mInput.read(new byte[mSize], 0, mSize);
+    }
+
+    private void checkHeaderReady() {
+        if (!mHeaderReady) {
+            throw new IllegalStateException("Entity header not read");
+        }
     }
 }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
index c7deada..e78a4b3 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
@@ -21,16 +21,29 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.ObjectOutputStream;
 
+/**
+ * Shadow for {@link BackupDataOutput}. Format written does NOT match implementation. To read data
+ * written with this shadow you should also declare shadow {@link ShadowBackupDataInput}.
+ */
 @Implements(BackupDataOutput.class)
 public class ShadowBackupDataOutput {
     private long mQuota;
     private int mTransportFlags;
+    private ObjectOutputStream mOutput;
 
     @Implementation
     public void __constructor__(FileDescriptor fd, long quota, int transportFlags) {
+        try {
+            mOutput = new ObjectOutputStream(new FileOutputStream(fd));
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
         mQuota = quota;
         mTransportFlags = transportFlags;
     }
@@ -47,11 +60,27 @@
 
     @Implementation
     public int writeEntityHeader(String key, int dataSize) throws IOException {
-        return 0;
+        final int size;
+        try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
+            writeEntityHeader(new ObjectOutputStream(byteStream), key, dataSize);
+            size = byteStream.size();
+        }
+        writeEntityHeader(mOutput, key, dataSize);
+        return size;
+    }
+
+    private void writeEntityHeader(ObjectOutputStream stream, String key, int dataSize)
+            throws IOException {
+        // Write the int first because readInt() throws EOFException, to know when stream ends
+        stream.writeInt(dataSize);
+        stream.writeUTF(key);
+        stream.flush();
     }
 
     @Implementation
     public int writeEntityData(byte[] data, int size) throws IOException {
-        return 0;
+        mOutput.write(data, 0, size);
+        mOutput.flush();
+        return size;
     }
 }
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5d8aca1..97d6c43 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -60,6 +60,7 @@
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index dcf5c27..1de67a5 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -33,47 +33,48 @@
 public final class DisconnectCause implements Parcelable {
 
     /** Disconnected because of an unknown or unspecified reason. */
-    public static final int UNKNOWN = 0;
+    public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0
     /** Disconnected because there was an error, such as a problem with the network. */
-    public static final int ERROR = 1;
+    public static final int ERROR = TelecomProtoEnums.ERROR; // = 1
     /** Disconnected because of a local user-initiated action, such as hanging up. */
-    public static final int LOCAL = 2;
+    public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2
     /**
      * Disconnected because of a remote user-initiated action, such as the other party hanging up
      * up.
      */
-    public static final int REMOTE = 3;
+    public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3
     /** Disconnected because it has been canceled. */
-    public static final int CANCELED = 4;
+    public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4
     /** Disconnected because there was no response to an incoming call. */
-    public static final int MISSED = 5;
+    public static final int MISSED = TelecomProtoEnums.MISSED; // = 5
     /** Disconnected because the user rejected an incoming call. */
-    public static final int REJECTED = 6;
+    public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6
     /** Disconnected because the other party was busy. */
-    public static final int BUSY = 7;
+    public static final int BUSY = TelecomProtoEnums.BUSY; // = 7
     /**
      * Disconnected because of a restriction on placing the call, such as dialing in airplane
      * mode.
      */
-    public static final int RESTRICTED = 8;
+    public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8
     /** Disconnected for reason not described by other disconnect codes. */
-    public static final int OTHER = 9;
+    public static final int OTHER = TelecomProtoEnums.OTHER; // = 9
     /**
      * Disconnected because the connection manager did not support the call. The call will be tried
      * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
      */
-    public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+    public static final int CONNECTION_MANAGER_NOT_SUPPORTED =
+            TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10
 
     /**
      * Disconnected because the user did not locally answer the incoming call, but it was answered
      * on another device where the call was ringing.
      */
-    public static final int ANSWERED_ELSEWHERE = 11;
+    public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11
 
     /**
      * Disconnected because the call was pulled from the current device to another device.
      */
-    public static final int CALL_PULLED = 12;
+    public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12
 
     /**
      * Reason code (returned via {@link #getReason()}) which indicates that a call could not be
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index 2591aaf..d62ef9e 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworkperf">
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-sdk android:minSdkVersion="5" />
 
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index c6824ec..8697f1b 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -5,6 +5,7 @@
     android:versionName="1.0" >
 
     <uses-sdk android:minSdkVersion="19"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />