Merge "Temporarily disable swipe up onboarding"
diff --git a/Android.bp b/Android.bp
index 704ec87..4ef0457 100644
--- a/Android.bp
+++ b/Android.bp
@@ -330,6 +330,8 @@
         "core/java/android/view/IPinnedStackController.aidl",
         "core/java/android/view/IPinnedStackListener.aidl",
         "core/java/android/view/IRemoteAnimationRunner.aidl",
+        "core/java/android/view/IRecentsAnimationController.aidl",
+        "core/java/android/view/IRecentsAnimationRunner.aidl",
         "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
         "core/java/android/view/IRotationWatcher.aidl",
         "core/java/android/view/IWallpaperVisibilityListener.aidl",
diff --git a/api/current.txt b/api/current.txt
index b869fe3..5f5c5ab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6,6 +6,7 @@
 
   public static final class Manifest.permission {
     ctor public Manifest.permission();
+    field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
     field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
     field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -1810,6 +1811,7 @@
   public static final class R.id {
     ctor public R.id();
     field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+    field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045
     field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
     field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
     field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
@@ -1818,6 +1820,7 @@
     field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
+    field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044
     field public static final int addToDictionary = 16908330; // 0x102002a
     field public static final int autofill = 16908355; // 0x1020043
     field public static final int background = 16908288; // 0x1020000
@@ -23115,6 +23118,7 @@
 
   public static final class MediaCodecInfo.EncoderCapabilities {
     method public android.util.Range<java.lang.Integer> getComplexityRange();
+    method public android.util.Range<java.lang.Integer> getQualityRange();
     method public boolean isBitrateModeSupported(int);
     field public static final int BITRATE_MODE_CBR = 2; // 0x2
     field public static final int BITRATE_MODE_CQ = 0; // 0x0
@@ -23457,6 +23461,7 @@
     field public static final java.lang.String KEY_PRIORITY = "priority";
     field public static final java.lang.String KEY_PROFILE = "profile";
     field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
+    field public static final java.lang.String KEY_QUALITY = "quality";
     field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
     field public static final java.lang.String KEY_ROTATION = "rotation-degrees";
     field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
@@ -40408,6 +40413,7 @@
     method public void onCallEvent(java.lang.String, android.os.Bundle);
     method public void onDisconnect();
     method public void onExtrasChanged(android.os.Bundle);
+    method public void onHandoverComplete();
     method public void onHold();
     method public void onPlayDtmfTone(char);
     method public void onPostDialContinue(boolean);
@@ -41101,6 +41107,8 @@
     method public void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
+    field public static final java.lang.String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+    field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -48688,6 +48696,7 @@
     method public java.lang.CharSequence getText();
     method public int getTextSelectionEnd();
     method public int getTextSelectionStart();
+    method public java.lang.CharSequence getTooltipText();
     method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter();
     method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore();
     method public java.lang.String getViewIdResourceName();
@@ -48775,6 +48784,7 @@
     method public void setSource(android.view.View, int);
     method public void setText(java.lang.CharSequence);
     method public void setTextSelection(int, int);
+    method public void setTooltipText(java.lang.CharSequence);
     method public void setTraversalAfter(android.view.View);
     method public void setTraversalAfter(android.view.View, int);
     method public void setTraversalBefore(android.view.View);
@@ -48844,6 +48854,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_MOVE_WINDOW;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
@@ -48863,6 +48874,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TOOLTIP;
   }
 
   public static final class AccessibilityNodeInfo.CollectionInfo {
@@ -49581,6 +49593,7 @@
     method public abstract boolean performEditorAction(int);
     method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
     method public abstract boolean reportFullscreenMode(boolean);
+    method public default void reportLanguageHint(android.os.LocaleList);
     method public abstract boolean requestCursorUpdates(int);
     method public abstract boolean sendKeyEvent(android.view.KeyEvent);
     method public abstract boolean setComposingRegion(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 58d06b3..7c26c1c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -253,6 +253,7 @@
   public class AppOpsManager {
     method public static java.lang.String[] getOpStrs();
     method public void setUidMode(java.lang.String, int, int);
+    field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final java.lang.String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -354,6 +355,19 @@
     method public org.json.JSONObject toJson() throws org.json.JSONException;
   }
 
+  public final class StatsManager {
+    method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+    method public byte[] getData(long);
+    method public byte[] getMetadata();
+    method public boolean removeConfiguration(long);
+    method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
+    field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+    field public static final java.lang.String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+    field public static final java.lang.String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
+    field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
+    field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
+  }
+
   public class VrManager {
     method public void setAndBindVrCompositor(android.content.ComponentName);
     method public void setPersistentVrModeEnabled(boolean);
@@ -3656,6 +3670,27 @@
     method public abstract void onResult(android.os.Bundle);
   }
 
+  public final class StatsDimensionsValue implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean getBooleanValue();
+    method public int getField();
+    method public float getFloatValue();
+    method public int getIntValue();
+    method public long getLongValue();
+    method public java.lang.String getStringValue();
+    method public java.util.List<android.os.StatsDimensionsValue> getTupleValueList();
+    method public int getValueType();
+    method public boolean isValueType(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BOOLEAN_VALUE_TYPE = 5; // 0x5
+    field public static final android.os.Parcelable.Creator<android.os.StatsDimensionsValue> CREATOR;
+    field public static final int FLOAT_VALUE_TYPE = 6; // 0x6
+    field public static final int INT_VALUE_TYPE = 3; // 0x3
+    field public static final int LONG_VALUE_TYPE = 4; // 0x4
+    field public static final int STRING_VALUE_TYPE = 2; // 0x2
+    field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
+  }
+
   public class SystemProperties {
     method public static java.lang.String get(java.lang.String);
     method public static java.lang.String get(java.lang.String, java.lang.String);
@@ -5096,16 +5131,6 @@
     method public int getUid();
   }
 
-  public final class StatsManager {
-    method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
-    method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
-    method public byte[] getData(java.lang.String);
-    method public byte[] getData(long);
-    method public byte[] getMetadata();
-    method public boolean removeConfiguration(java.lang.String);
-    method public boolean removeConfiguration(long);
-  }
-
 }
 
 package android.view {
diff --git a/api/test-current.txt b/api/test-current.txt
index c1f1bb6..4e8f904 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -48,6 +48,7 @@
   public class AppOpsManager {
     method public static java.lang.String[] getOpStrs();
     method public void setMode(int, int, java.lang.String, int);
+    field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final java.lang.String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 9200f64..565b092 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -62,6 +62,7 @@
     src/storage/StorageManager.cpp \
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
+    src/subscriber/SubscriberReporter.cpp \
     src/HashableDimensionKey.cpp \
     src/guardrail/MemoryLeakTrackUtil.cpp \
     src/guardrail/StatsdStats.cpp
@@ -136,7 +137,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-LOCAL_INIT_RC := statsd.rc
+#LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 8975c54..31994e1 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -24,6 +24,7 @@
 #include "guardrail/MemoryLeakTrackUtil.h"
 #include "guardrail/StatsdStats.h"
 #include "storage/StorageManager.h"
+#include "subscriber/SubscriberReporter.h"
 
 #include <android-base/file.h>
 #include <binder/IPCThreadState.h>
@@ -67,6 +68,7 @@
 void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
     ALOGW("statscompanion service died");
     mAnomalyMonitor->setStatsCompanionService(nullptr);
+    SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
 }
 
 // ======================================================================
@@ -683,6 +685,7 @@
     VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
     IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
     mAnomalyMonitor->setStatsCompanionService(statsCompanion);
+    SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
 
     return Status::ok();
 }
@@ -745,7 +748,9 @@
 Status StatsService::removeConfiguration(int64_t key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), key));
+        ConfigKey configKey(ipc->getCallingUid(), key);
+        mConfigManager->RemoveConfig(configKey);
+        SubscriberReporter::getInstance().removeConfig(configKey);
         *success = true;
         return Status::ok();
     } else {
@@ -754,6 +759,42 @@
     }
 }
 
+Status StatsService::setBroadcastSubscriber(int64_t configId,
+                                            int64_t subscriberId,
+                                            const sp<android::IBinder>& intentSender,
+                                            bool* success) {
+    VLOG("StatsService::setBroadcastSubscriber called.");
+    IPCThreadState* ipc = IPCThreadState::self();
+    if (checkCallingPermission(String16(kPermissionDump))) {
+        ConfigKey configKey(ipc->getCallingUid(), configId);
+        SubscriberReporter::getInstance()
+                .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+        *success = true;
+        return Status::ok();
+    } else {
+        *success = false;
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+    }
+}
+
+Status StatsService::unsetBroadcastSubscriber(int64_t configId,
+                                              int64_t subscriberId,
+                                              bool* success) {
+    VLOG("StatsService::unsetBroadcastSubscriber called.");
+    IPCThreadState* ipc = IPCThreadState::self();
+    if (checkCallingPermission(String16(kPermissionDump))) {
+        ConfigKey configKey(ipc->getCallingUid(), configId);
+        SubscriberReporter::getInstance()
+                .unsetBroadcastSubscriber(configKey, subscriberId);
+        *success = true;
+        return Status::ok();
+    } else {
+        *success = false;
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+    }
+}
+
+
 void StatsService::binderDied(const wp <IBinder>& who) {
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 8d29970..ba6bd24 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -99,6 +99,21 @@
      */
     virtual Status removeConfiguration(int64_t key, bool* success) override;
 
+    /**
+     * Binder call to associate the given config's subscriberId with the given intentSender.
+     * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+     */
+    virtual Status setBroadcastSubscriber(int64_t configId,
+                                          int64_t subscriberId,
+                                          const sp<android::IBinder>& intentSender,
+                                          bool* success) override;
+
+    /**
+     * Binder call to unassociate the given config's subscriberId with any intentSender.
+     */
+    virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId,
+                                            bool* success) override;
+
     // TODO: public for testing since statsd doesn't run when system starts. Change to private
     // later.
     /** Inform statsCompanion that statsd is ready. */
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index e34aed3..ded6c4c 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -21,6 +21,7 @@
 #include "external/Perfetto.h"
 #include "guardrail/StatsdStats.h"
 #include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+#include "subscriber/SubscriberReporter.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -233,6 +234,7 @@
     }
 
     std::set<int> incidentdSections;
+
     for (const Subscription& subscription : mSubscriptions) {
         switch (subscription.subscriber_information_case()) {
             case Subscription::SubscriberInformationCase::kIncidentdDetails:
@@ -243,6 +245,10 @@
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
                 break;
+            case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
+                SubscriberReporter::getInstance()
+                        .alertBroadcastSubscriber(mConfigKey, subscription, key);
+                break;
             default:
                 break;
         }
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index de7093d..33e55ab 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -51,10 +51,8 @@
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
     // and removes it from firedAlarms.
-    // TODO: This will actually be called from a different thread, so make it thread-safe!
-    //          This means that almost every function in DurationAnomalyTracker needs to be locked.
-    //          But this should be done at the level of StatsLogProcessor, which needs to lock
-    //          mMetricsMangers anyway.
+    // Note that this will generally be called from a different thread from the other functions;
+    // the caller is responsible for thread safety.
     void informAlarmsFired(const uint64_t& timestampNs,
             unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) override;
 
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
new file mode 100644
index 0000000..f912e4b
--- /dev/null
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "SubscriberReporter.h"
+
+using android::IBinder;
+using std::lock_guard;
+using std::unordered_map;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
+                                                int64_t subscriberId,
+                                                const sp<IBinder>& intentSender) {
+    VLOG("SubscriberReporter::setBroadcastSubscriber called.");
+    lock_guard<std::mutex> lock(mLock);
+    mIntentMap[configKey][subscriberId] = intentSender;
+}
+
+void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
+                                                  int64_t subscriberId) {
+    VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
+    lock_guard<std::mutex> lock(mLock);
+    auto subscriberMapIt = mIntentMap.find(configKey);
+    if (subscriberMapIt != mIntentMap.end()) {
+        subscriberMapIt->second.erase(subscriberId);
+        if (subscriberMapIt->second.empty()) {
+            mIntentMap.erase(configKey);
+        }
+    }
+}
+
+void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
+    VLOG("SubscriberReporter::removeConfig called.");
+    lock_guard<std::mutex> lock(mLock);
+    mIntentMap.erase(configKey);
+}
+
+void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
+                                                  const Subscription& subscription,
+                                                  const HashableDimensionKey& dimKey) const {
+    // Reminder about ids:
+    //  subscription id - name of the Subscription (that ties the Alert to the broadcast)
+    //  subscription rule_id - the name of the Alert (that triggers the broadcast)
+    //  subscriber_id - name of the PendingIntent to use to send the broadcast
+    //  config uid - the uid that uploaded the config (and therefore gave the PendingIntent,
+    //                 although the intent may be to broadcast to a different uid)
+    //  config id - the name of this config (for this particular uid)
+
+    VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
+    lock_guard<std::mutex> lock(mLock);
+
+    if (!subscription.has_broadcast_subscriber_details()
+            || !subscription.broadcast_subscriber_details().has_subscriber_id()) {
+        ALOGE("Broadcast subscriber does not have an id.");
+        return;
+    }
+    int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
+
+    auto it1 = mIntentMap.find(configKey);
+    if (it1 == mIntentMap.end()) {
+        ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str());
+        return;
+    }
+    auto it2 = it1->second.find(subscriberId);
+    if (it2 == it1->second.end()) {
+        ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ",
+                configKey.ToString().c_str(), (long long)subscriberId);
+        return;
+    }
+    sendBroadcastLocked(it2->second, configKey, subscription, dimKey);
+}
+
+void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
+                                             const ConfigKey& configKey,
+                                             const Subscription& subscription,
+                                             const HashableDimensionKey& dimKey) const {
+    VLOG("SubscriberReporter::sendBroadcastLocked called.");
+    if (mStatsCompanionService == nullptr) {
+        ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
+        return;
+    }
+    mStatsCompanionService->sendSubscriberBroadcast(intentSender,
+                                                    configKey.GetUid(),
+                                                    configKey.GetId(),
+                                                    subscription.id(),
+                                                    subscription.rule_id(),
+                                                    protoToStatsDimensionsValue(dimKey));
+}
+
+StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
+        const HashableDimensionKey& dimKey) {
+    return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+}
+
+StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
+        const DimensionsValue& protoDimsVal) {
+    int32_t field = protoDimsVal.field();
+
+    switch (protoDimsVal.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return StatsDimensionsValue(field, String16(protoDimsVal.value_str().c_str()));
+        case DimensionsValue::ValueCase::kValueInt:
+            return StatsDimensionsValue(field, static_cast<int32_t>(protoDimsVal.value_int()));
+        case DimensionsValue::ValueCase::kValueLong:
+            return StatsDimensionsValue(field, static_cast<int64_t>(protoDimsVal.value_long()));
+        case DimensionsValue::ValueCase::kValueBool:
+            return StatsDimensionsValue(field, static_cast<bool>(protoDimsVal.value_bool()));
+        case DimensionsValue::ValueCase::kValueFloat:
+            return StatsDimensionsValue(field, static_cast<float>(protoDimsVal.value_float()));
+        case DimensionsValue::ValueCase::kValueTuple:
+            {
+                int sz = protoDimsVal.value_tuple().dimensions_value_size();
+                std::vector<StatsDimensionsValue> sdvVec(sz);
+                for (int i = 0; i < sz; i++) {
+                    sdvVec[i] = protoToStatsDimensionsValue(
+                            protoDimsVal.value_tuple().dimensions_value(i));
+                }
+                return StatsDimensionsValue(field, sdvVec);
+            }
+        default:
+            ALOGW("protoToStatsDimensionsValue failed: illegal type.");
+            return StatsDimensionsValue();
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
new file mode 100644
index 0000000..5bb458a
--- /dev/null
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/os/IStatsCompanionService.h>
+#include <utils/RefBase.h>
+
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"  // DimensionsValue
+#include "android/os/StatsDimensionsValue.h"
+#include "HashableDimensionKey.h"
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Reports information to subscribers.
+// Single instance shared across the process. All methods are thread safe.
+class SubscriberReporter {
+public:
+    /** Get (singleton) instance of SubscriberReporter. */
+    static SubscriberReporter& getInstance() {
+        static SubscriberReporter subscriberReporter;
+        return subscriberReporter;
+    }
+
+    ~SubscriberReporter(){};
+    SubscriberReporter(SubscriberReporter const&) = delete;
+    void operator=(SubscriberReporter const&) = delete;
+
+    /**
+     * Tells SubscriberReporter what IStatsCompanionService to use.
+     * May be nullptr, but SubscriberReporter will not send broadcasts for any calls
+     * to alertBroadcastSubscriber that occur while nullptr.
+     */
+    void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+        std::lock_guard<std::mutex> lock(mLock);
+        sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+        mStatsCompanionService = statsCompanionService;
+    }
+
+    /**
+     * Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
+     * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+     */
+    void setBroadcastSubscriber(const ConfigKey& configKey,
+                                int64_t subscriberId,
+                                const sp<android::IBinder>& intentSender);
+
+    /**
+     * Erases any intentSender information from the given (configKey, subscriberId) pair.
+     */
+    void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+
+    /** Remove all information stored by SubscriberReporter about the given config. */
+    void removeConfig(const ConfigKey& configKey);
+
+    /**
+     * Sends a broadcast via the intentSender previously stored for the
+     * given (configKey, subscriberId) pair by setBroadcastSubscriber.
+     * Information about the subscriber, as well as information extracted from the dimKey, is sent.
+     */
+    void alertBroadcastSubscriber(const ConfigKey& configKey,
+                                  const Subscription& subscription,
+                                  const HashableDimensionKey& dimKey) const;
+
+private:
+    SubscriberReporter() {};
+
+    mutable std::mutex mLock;
+
+    /** Binder interface for communicating with StatsCompanionService. */
+    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+    /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
+    std::unordered_map<ConfigKey,
+            std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
+
+    /**
+     * Sends a broadcast via the given intentSender (using mStatsCompanionService), along
+     * with the information in the other parameters.
+     */
+    void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
+                             const ConfigKey& configKey,
+                             const Subscription& subscription,
+                             const HashableDimensionKey& dimKey) const;
+
+    /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
+    static StatsDimensionsValue protoToStatsDimensionsValue(
+            const DimensionsValue& protoDimsVal);
+
+    /** Converts a HashableDimensionKey to a StatsDimensionsValue. */
+    static StatsDimensionsValue protoToStatsDimensionsValue(
+            const HashableDimensionKey& dimKey);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 4f9032f..d39aa1d 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -16,12 +16,12 @@
 package com.android.statsd.dogfood;
 
 import android.app.Activity;
+import android.app.StatsManager;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.Log;
 import android.util.StatsLog;
-import android.util.StatsManager;
 import android.view.View;
 import android.widget.TextView;
 import android.widget.Toast;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index 26c1c72..652f6b2 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,7 +35,6 @@
 import android.text.TextWatcher;
 import android.util.Log;
 import android.util.StatsLog;
-import android.util.StatsManager;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.view.MotionEvent;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4bcd677..fee5827 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -207,6 +207,12 @@
             "android.activity.taskOverlayCanResume";
 
     /**
+     * See {@link #setAvoidMoveToFront()}.
+     * @hide
+     */
+    private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront";
+
+    /**
      * Where the split-screen-primary stack should be positioned.
      * @hide
      */
@@ -307,6 +313,7 @@
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mTaskOverlay;
     private boolean mTaskOverlayCanResume;
+    private boolean mAvoidMoveToFront;
     private AppTransitionAnimationSpec mAnimSpecs[];
     private int mRotationAnimationHint = -1;
     private Bundle mAppVerificationBundle;
@@ -923,6 +930,7 @@
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
         mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
         mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
+        mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
         mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
                 SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
         mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
@@ -1239,6 +1247,25 @@
         return mTaskOverlayCanResume;
     }
 
+    /**
+     * Sets whether the activity launched should not cause the activity stack it is contained in to
+     * be moved to the front as a part of launching.
+     *
+     * @hide
+     */
+    public void setAvoidMoveToFront() {
+        mAvoidMoveToFront = true;
+    }
+
+    /**
+     * @return whether the activity launch should prevent moving the associated activity stack to
+     *         the front.
+     * @hide
+     */
+    public boolean getAvoidMoveToFront() {
+        return mAvoidMoveToFront;
+    }
+
     /** @hide */
     public int getSplitScreenCreateMode() {
         return mSplitScreenCreateMode;
@@ -1416,6 +1443,7 @@
         b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
         b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
         b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
+        b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
         b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
         b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
                 mDisallowEnterPictureInPictureWhileLaunching);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7ca6802..e923fb2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -263,8 +263,10 @@
     public static final int OP_REQUEST_DELETE_PACKAGES = 72;
     /** @hide Bind an accessibility service. */
     public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
+    /** @hide Continue handover of a call from another app */
+    public static final int OP_ACCEPT_HANDOVER = 74;
     /** @hide */
-    public static final int _NUM_OP = 74;
+    public static final int _NUM_OP = 75;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -378,7 +380,13 @@
     /** Answer incoming phone calls */
     public static final String OPSTR_ANSWER_PHONE_CALLS
             = "android:answer_phone_calls";
-
+    /**
+     * Accept call handover
+     * @hide
+     */
+    @SystemApi @TestApi
+    public static final String OPSTR_ACCEPT_HANDOVER
+            = "android:accept_handover";
     /** @hide */
     @SystemApi @TestApi
     public static final String OPSTR_GPS = "android:gps";
@@ -528,6 +536,7 @@
             OP_USE_SIP,
             OP_PROCESS_OUTGOING_CALLS,
             OP_ANSWER_PHONE_CALLS,
+            OP_ACCEPT_HANDOVER,
             // Microphone
             OP_RECORD_AUDIO,
             // Camera
@@ -626,6 +635,7 @@
             OP_CHANGE_WIFI_STATE,
             OP_REQUEST_DELETE_PACKAGES,
             OP_BIND_ACCESSIBILITY_SERVICE,
+            OP_ACCEPT_HANDOVER,
     };
 
     /**
@@ -706,6 +716,7 @@
             OPSTR_CHANGE_WIFI_STATE,
             OPSTR_REQUEST_DELETE_PACKAGES,
             OPSTR_BIND_ACCESSIBILITY_SERVICE,
+            OPSTR_ACCEPT_HANDOVER,
     };
 
     /**
@@ -787,6 +798,7 @@
             "CHANGE_WIFI_STATE",
             "REQUEST_DELETE_PACKAGES",
             "BIND_ACCESSIBILITY_SERVICE",
+            "ACCEPT_HANDOVER",
     };
 
     /**
@@ -868,6 +880,7 @@
             Manifest.permission.CHANGE_WIFI_STATE,
             Manifest.permission.REQUEST_DELETE_PACKAGES,
             Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
+            Manifest.permission.ACCEPT_HANDOVER,
     };
 
     /**
@@ -950,6 +963,7 @@
             null, // OP_CHANGE_WIFI_STATE
             null, // REQUEST_DELETE_PACKAGES
             null, // OP_BIND_ACCESSIBILITY_SERVICE
+            null, // ACCEPT_HANDOVER
     };
 
     /**
@@ -1031,6 +1045,7 @@
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
+            false, // ACCEPT_HANDOVER
     };
 
     /**
@@ -1111,6 +1126,7 @@
             AppOpsManager.MODE_ALLOWED,  // OP_CHANGE_WIFI_STATE
             AppOpsManager.MODE_ALLOWED,  // REQUEST_DELETE_PACKAGES
             AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
+            AppOpsManager.MODE_ALLOWED,  // ACCEPT_HANDOVER
     };
 
     /**
@@ -1195,6 +1211,7 @@
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
+            false, // ACCEPT_HANDOVER
     };
 
     /**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 9c15562..56bc184 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -65,6 +65,7 @@
 import android.os.StrictMode;
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
+import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationDefinition;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
@@ -441,8 +442,9 @@
             in Bundle options, int userId);
     int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
             in Intent intent, in String resolvedType, in Bundle options, int userId);
-    int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options,
-            in Bundle activityOptions, int userId);
+    void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
+            in IRecentsAnimationRunner recentsAnimationRunner);
+    void cancelRecentsAnimation();
     int startActivityFromRecents(int taskId, in Bundle options);
     Bundle getActivityOptions(in IBinder token);
     List<IBinder> getAppTasks(in String callingPackage);
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
new file mode 100644
index 0000000..963fc77
--- /dev/null
+++ b/core/java/android/app/StatsManager.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.IStatsManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+/**
+ * API for statsd clients to send configurations and retrieve data.
+ *
+ * @hide
+ */
+@SystemApi
+public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
+    IStatsManager mService;
+    private static final String TAG = "StatsManager";
+
+    /** Long extra of uid that added the relevant stats config. */
+    public static final String EXTRA_STATS_CONFIG_UID =
+            "android.app.extra.STATS_CONFIG_UID";
+    /** Long extra of the relevant stats config's configKey. */
+    public static final String EXTRA_STATS_CONFIG_KEY =
+            "android.app.extra.STATS_CONFIG_KEY";
+    /** Long extra of the relevant statsd_config.proto's Subscription.id. */
+    public static final String EXTRA_STATS_SUBSCRIPTION_ID =
+            "android.app.extra.STATS_SUBSCRIPTION_ID";
+    /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
+    public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
+            "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
+    /**
+     * Extra of a {@link android.os.StatsDimensionsValue} representing sliced dimension value
+     * information.
+     */
+    public static final String EXTRA_STATS_DIMENSIONS_VALUE =
+            "android.app.extra.STATS_DIMENSIONS_VALUE";
+
+    /**
+     * Constructor for StatsManagerClient.
+     *
+     * @hide
+     */
+    public StatsManager() {
+    }
+
+    /**
+     * Clients can send a configuration and simultaneously registers the name of a broadcast
+     * receiver that listens for when it should request data.
+     *
+     * @param configKey An arbitrary integer that allows clients to track the configuration.
+     * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
+     *                  dependencies eg, conditions and matchers).
+     * @param pkg       The package name to receive the broadcast.
+     * @param cls       The name of the class that receives the broadcast.
+     * @return true if successful
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when adding configuration");
+                    return false;
+                }
+                return service.addConfiguration(configKey, config, pkg, cls);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Remove a configuration from logging.
+     *
+     * @param configKey Configuration key to remove.
+     * @return true if successful
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean removeConfiguration(long configKey) {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when removing configuration");
+                    return false;
+                }
+                return service.removeConfiguration(configKey);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Set the PendingIntent to be used when broadcasting subscriber information to the given
+     * subscriberId within the given config.
+     *
+     * <p>
+     * Suppose that the calling uid has added a config with key configKey, and that in this config
+     * it is specified that when a particular anomaly is detected, a broadcast should be sent to
+     * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
+     * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
+     * when the anomaly is detected.
+     *
+     * <p>
+     * When statsd sends the broadcast, the PendingIntent will used to send an intent with
+     * information of
+     *   {@link #EXTRA_STATS_CONFIG_UID},
+     *   {@link #EXTRA_STATS_CONFIG_KEY},
+     *   {@link #EXTRA_STATS_SUBSCRIPTION_ID},
+     *   {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
+     *   {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
+     *
+     * <p>
+     * This function can only be called by the owner (uid) of the config. It must be called each
+     * time statsd starts. The config must have been added first (via addConfiguration()).
+     *
+     * @param configKey The integer naming the config to which this subscriber is attached.
+     * @param subscriberId ID of the subscriber, as used in the config.
+     * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
+     *                      associated with the given subscriberId. May be null, in which case
+     *                      it undoes any previous setting of this subscriberId.
+     * @return true if successful
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean setBroadcastSubscriber(long configKey,
+                                          long subscriberId,
+                                          PendingIntent pendingIntent) {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.w(TAG, "Failed to find statsd when adding broadcast subscriber");
+                    return false;
+                }
+                if (pendingIntent != null) {
+                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+                    IBinder intentSender = pendingIntent.getTarget().asBinder();
+                    return service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
+                } else {
+                    return service.unsetBroadcastSubscriber(configKey, subscriberId);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Clients can request data with a binder call. This getter is destructive and also clears
+     * the retrieved metrics from statsd memory.
+     *
+     * @param configKey Configuration key to retrieve data from.
+     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getData(long configKey) {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when getting data");
+                    return null;
+                }
+                return service.getData(configKey);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connecto statsd when getting data");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Clients can request metadata for statsd. Will contain stats across all configurations but not
+     * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
+     * This getter is not destructive and will not reset any metrics/counters.
+     *
+     * @return Serialized StatsdStatsReport proto. Returns null on failure.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getMetadata() {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when getting metadata");
+                    return null;
+                }
+                return service.getMetadata();
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+                return null;
+            }
+        }
+    }
+
+    private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            synchronized (this) {
+                mService = null;
+            }
+        }
+    }
+
+    private IStatsManager getIStatsManagerLocked() throws RemoteException {
+        if (mService != null) {
+            return mService;
+        }
+        mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+        if (mService != null) {
+            mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+        }
+        return mService;
+    }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fb8d101..4310434 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -141,7 +141,6 @@
 import android.telephony.euicc.EuiccCardManager;
 import android.telephony.euicc.EuiccManager;
 import android.util.Log;
-import android.util.StatsManager;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f69aab01..1b05033 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4121,7 +4121,7 @@
     public static final String STATS_COMPANION_SERVICE = "statscompanion";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve an {@link android.stats.StatsManager}.
+     * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1a4765b..8e05cfa 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -892,7 +892,7 @@
      *
      * @hide
      */
-    private Set<UidRange> mUids = null;
+    private ArraySet<UidRange> mUids = null;
 
     /**
      * Convenience method to set the UIDs this network applies to to a single UID.
@@ -1178,7 +1178,7 @@
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
         dest.writeInt(mSignalStrength);
-        dest.writeArraySet(new ArraySet<>(mUids));
+        dest.writeArraySet(mUids);
     }
 
     public static final Creator<NetworkCapabilities> CREATOR =
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 1d2a408..8a27700 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.os.StatsDimensionsValue;
 import android.os.StatsLogEventWrapper;
 
 /**
@@ -55,8 +56,17 @@
     StatsLogEventWrapper[] pullData(int pullCode);
 
     /** Send a broadcast to the specified pkg and class that it should getData now. */
+    // TODO: Rename this and use a pending intent instead.
     oneway void sendBroadcast(String pkg, String cls);
 
+    /**
+     * Requests StatsCompanionService to send a broadcast using the given intentSender
+     * (which should cast to an IIntentSender), along with the other information specified.
+     */
+    oneway void sendSubscriberBroadcast(in IBinder intentSender, long configUid, long configId,
+                                        long subscriptionId, long subscriptionRuleId,
+                                        in StatsDimensionsValue dimensionsValue);
+
     /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
     oneway void triggerUidSnapshot();
 }
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 29812e8..679b49d 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -81,7 +81,7 @@
     /**
      * Sets a configuration with the specified config key and subscribes to updates for this
      * configuration key. Broadcasts will be sent if this configuration needs to be collected.
-     * The configuration must be a wire-encoded StatsDConfig. The caller specifies the name of the
+     * The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the
      * package and class that should receive these broadcasts.
      *
      * Returns if this configuration was correctly registered.
@@ -95,4 +95,33 @@
      * Returns if this configuration key was removed.
      */
     boolean removeConfiguration(in long configKey);
+
+    /**
+     * Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
+     * information to the given subscriberId within the given config.
+     *
+     * Suppose that the calling uid has added a config with key configKey, and that in this config
+     * it is specified that when a particular anomaly is detected, a broadcast should be sent to
+     * a BroadcastSubscriber with id subscriberId. This function links the given intentSender with
+     * that subscriberId (for that config), so that this intentSender is used to send the broadcast
+     * when the anomaly is detected.
+     *
+     * This function can only be called by the owner (uid) of the config. It must be called each
+     * time statsd starts. Later calls overwrite previous calls; only one intentSender is stored.
+     *
+     * intentSender must be convertible into an IntentSender using IntentSender(IBinder)
+     * and cannot be null.
+     *
+     * Returns true if successful.
+     */
+    boolean setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
+
+    /**
+     * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
+     * Any broadcasts associated with subscriberId will henceforth not be sent.
+     * No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
+     *
+     * Returns true if successful.
+     */
+    boolean unsetBroadcastSubscriber(long configKey, long subscriberId);
 }
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/core/java/android/os/StatsDimensionsValue.aidl
new file mode 100644
index 0000000..81a14a4
--- /dev/null
+++ b/core/java/android/os/StatsDimensionsValue.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** @hide */
+parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
diff --git a/core/java/android/os/StatsDimensionsValue.java b/core/java/android/os/StatsDimensionsValue.java
new file mode 100644
index 0000000..257cc52
--- /dev/null
+++ b/core/java/android/os/StatsDimensionsValue.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.SystemApi;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Container for statsd dimension value information, corresponding to a
+ * stats_log.proto's DimensionValue.
+ *
+ * This consists of a field (an int representing a statsd atom field)
+ * and a value (which may be one of a number of types).
+ *
+ * <p>
+ * Only a single value is held, and it is necessarily one of the following types:
+ * {@link String}, int, long, boolean, float,
+ * or tuple (i.e. {@link List} of {@code StatsDimensionsValue}).
+ *
+ * The type of value held can be retrieved using {@link #getValueType()}, which returns one of the
+ * following ints, depending on the type of value:
+ * <ul>
+ *  <li>{@link #STRING_VALUE_TYPE}</li>
+ *  <li>{@link #INT_VALUE_TYPE}</li>
+ *  <li>{@link #LONG_VALUE_TYPE}</li>
+ *  <li>{@link #BOOLEAN_VALUE_TYPE}</li>
+ *  <li>{@link #FLOAT_VALUE_TYPE}</li>
+ *  <li>{@link #TUPLE_VALUE_TYPE}</li>
+ * </ul>
+ * Alternatively, this can be determined using {@link #isValueType(int)} with one of these constants
+ * as a parameter.
+ * The value itself can be retrieved using the correct get...Value() function for its type.
+ *
+ * <p>
+ * The field is always an int, and always exists; it can be obtained using {@link #getField()}.
+ *
+ *
+ * @hide
+ */
+@SystemApi
+public final class StatsDimensionsValue implements Parcelable {
+    private static final String TAG = "StatsDimensionsValue";
+
+    // Values of the value type correspond to stats_log.proto's DimensionValue fields.
+    // Keep constants in sync with services/include/android/os/StatsDimensionsValue.h.
+    /** Indicates that this holds a String. */
+    public static final int STRING_VALUE_TYPE = 2;
+    /** Indicates that this holds an int. */
+    public static final int INT_VALUE_TYPE = 3;
+    /** Indicates that this holds a long. */
+    public static final int LONG_VALUE_TYPE = 4;
+    /** Indicates that this holds a boolean. */
+    public static final int BOOLEAN_VALUE_TYPE = 5;
+    /** Indicates that this holds a float. */
+    public static final int FLOAT_VALUE_TYPE = 6;
+    /** Indicates that this holds a List of StatsDimensionsValues. */
+    public static final int TUPLE_VALUE_TYPE = 7;
+
+    /** Value of a stats_log.proto DimensionsValue.field. */
+    private final int mField;
+
+    /** Type of stats_log.proto DimensionsValue.value, according to the VALUE_TYPEs above. */
+    private final int mValueType;
+
+    /**
+     * Value of a stats_log.proto DimensionsValue.value.
+     * String, Integer, Long, Boolean, Float, or StatsDimensionsValue[].
+     */
+    private final Object mValue; // immutable or array of immutables
+
+    /**
+     * Creates a {@code StatsDimensionValue} from a parcel.
+     *
+     * @hide
+     */
+    public StatsDimensionsValue(Parcel in) {
+        mField = in.readInt();
+        mValueType = in.readInt();
+        mValue = readValueFromParcel(mValueType, in);
+    }
+
+    /**
+     * Return the field, i.e. the tag of a statsd atom.
+     *
+     * @return the field
+     */
+    public int getField() {
+        return mField;
+    }
+
+    /**
+     * Retrieve the String held, if any.
+     *
+     * @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE},
+     *         null otherwise
+     */
+    public String getStringValue() {
+        try {
+            if (mValueType == STRING_VALUE_TYPE) return (String) mValue;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the int held, if any.
+     *
+     * @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise
+     */
+    public int getIntValue() {
+        try {
+            if (mValueType == INT_VALUE_TYPE) return (Integer) mValue;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Retrieve the long held, if any.
+     *
+     * @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise
+     */
+    public long getLongValue() {
+        try {
+            if (mValueType == LONG_VALUE_TYPE) return (Long) mValue;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Retrieve the boolean held, if any.
+     *
+     * @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE},
+     *         false otherwise
+     */
+    public boolean getBooleanValue() {
+        try {
+            if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve the float held, if any.
+     *
+     * @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise
+     */
+    public float getFloatValue() {
+        try {
+            if (mValueType == FLOAT_VALUE_TYPE) return (Float) mValue;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Retrieve the tuple, in the form of a {@link List} of {@link StatsDimensionsValue}, held,
+     * if any.
+     *
+     * @return the {@link List} of {@link StatsDimensionsValue} held
+     *         if {@link #getValueType()} == {@link #TUPLE_VALUE_TYPE},
+     *         null otherwise
+     */
+    public List<StatsDimensionsValue> getTupleValueList() {
+        if (mValueType != TUPLE_VALUE_TYPE) {
+            return null;
+        }
+        try {
+            StatsDimensionsValue[] orig = (StatsDimensionsValue[]) mValue;
+            List<StatsDimensionsValue> copy = new ArrayList<>(orig.length);
+            // Shallow copy since StatsDimensionsValue is immutable anyway
+            for (int i = 0; i < orig.length; i++) {
+                copy.add(orig[i]);
+            }
+            return copy;
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the constant representing the type of value stored, namely one of
+     * <ul>
+     *   <li>{@link #STRING_VALUE_TYPE}</li>
+     *   <li>{@link #INT_VALUE_TYPE}</li>
+     *   <li>{@link #LONG_VALUE_TYPE}</li>
+     *   <li>{@link #BOOLEAN_VALUE_TYPE}</li>
+     *   <li>{@link #FLOAT_VALUE_TYPE}</li>
+     *   <li>{@link #TUPLE_VALUE_TYPE}</li>
+     * </ul>
+     *
+     * @return the constant representing the type of value stored
+     */
+    public int getValueType() {
+        return mValueType;
+    }
+
+    /**
+     * Returns whether the type of value stored is equal to the given type.
+     *
+     * @param valueType int representing the type of value stored, as used in {@link #getValueType}
+     * @return true if {@link #getValueType()} is equal to {@code valueType}.
+     */
+    public boolean isValueType(int valueType) {
+        return mValueType == valueType;
+    }
+
+    /**
+     * Returns a String representing the information in this StatsDimensionValue.
+     * No guarantees are made about the format of this String.
+     *
+     * @return String representation
+     *
+     * @hide
+     */
+    // Follows the format of statsd's dimension.h toString.
+    public String toString() {
+        try {
+            StringBuilder sb = new StringBuilder();
+            sb.append(mField);
+            sb.append(":");
+            if (mValueType == TUPLE_VALUE_TYPE) {
+                sb.append("{");
+                StatsDimensionsValue[] sbvs = (StatsDimensionsValue[]) mValue;
+                for (int i = 0; i < sbvs.length; i++) {
+                    sb.append(sbvs[i].toString());
+                    sb.append("|");
+                }
+                sb.append("}");
+            } else {
+                sb.append(mValue.toString());
+            }
+            return sb.toString();
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Failed to successfully get value", e);
+        }
+        return "";
+    }
+
+    /**
+     * Parcelable Creator for StatsDimensionsValue.
+     */
+    public static final Parcelable.Creator<StatsDimensionsValue> CREATOR = new
+            Parcelable.Creator<StatsDimensionsValue>() {
+                public StatsDimensionsValue createFromParcel(Parcel in) {
+                    return new StatsDimensionsValue(in);
+                }
+
+                public StatsDimensionsValue[] newArray(int size) {
+                    return new StatsDimensionsValue[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mField);
+        out.writeInt(mValueType);
+        writeValueToParcel(mValueType, mValue, out, flags);
+    }
+
+    /** Writes mValue to a parcel. Returns true if succeeds. */
+    private static boolean writeValueToParcel(int valueType, Object value, Parcel out, int flags) {
+        try {
+            switch (valueType) {
+                case STRING_VALUE_TYPE:
+                    out.writeString((String) value);
+                    return true;
+                case INT_VALUE_TYPE:
+                    out.writeInt((Integer) value);
+                    return true;
+                case LONG_VALUE_TYPE:
+                    out.writeLong((Long) value);
+                    return true;
+                case BOOLEAN_VALUE_TYPE:
+                    out.writeBoolean((Boolean) value);
+                    return true;
+                case FLOAT_VALUE_TYPE:
+                    out.writeFloat((Float) value);
+                    return true;
+                case TUPLE_VALUE_TYPE: {
+                    StatsDimensionsValue[] values = (StatsDimensionsValue[]) value;
+                    out.writeInt(values.length);
+                    for (int i = 0; i < values.length; i++) {
+                        values[i].writeToParcel(out, flags);
+                    }
+                    return true;
+                }
+                default:
+                    Slog.w(TAG, "readValue of an impossible type " + valueType);
+                    return false;
+            }
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "writeValue cast failed", e);
+            return false;
+        }
+    }
+
+    /** Reads mValue from a parcel. */
+    private static Object readValueFromParcel(int valueType, Parcel parcel) {
+        switch (valueType) {
+            case STRING_VALUE_TYPE:
+                return parcel.readString();
+            case INT_VALUE_TYPE:
+                return parcel.readInt();
+            case LONG_VALUE_TYPE:
+                return parcel.readLong();
+            case BOOLEAN_VALUE_TYPE:
+                return parcel.readBoolean();
+            case FLOAT_VALUE_TYPE:
+                return parcel.readFloat();
+            case TUPLE_VALUE_TYPE: {
+                final int sz = parcel.readInt();
+                StatsDimensionsValue[] values = new StatsDimensionsValue[sz];
+                for (int i = 0; i < sz; i++) {
+                    values[i] = new StatsDimensionsValue(parcel);
+                }
+                return values;
+            }
+            default:
+                Slog.w(TAG, "readValue of an impossible type " + valueType);
+                return null;
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index baa90e75..1ea4861 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9155,6 +9155,22 @@
                BOOLEAN_VALIDATOR;
 
        /**
+        * Whether to notify the user of carrier networks.
+        * <p>
+        * If not connected and the scan results have a carrier network, we will
+        * put this notification up. If we attempt to connect to a network or
+        * the carrier network(s) disappear, we remove the notification. When we
+        * show the notification, we will not show it again for
+        * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} time.
+        * @hide
+        */
+       public static final String WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+               "wifi_carrier_networks_available_notification_on";
+
+       private static final Validator WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+               BOOLEAN_VALIDATOR;
+
+       /**
         * {@hide}
         */
        public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
@@ -11261,6 +11277,7 @@
             NETWORK_RECOMMENDATIONS_ENABLED,
             WIFI_WAKEUP_ENABLED,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+            WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             USE_OPEN_WIFI_PACKAGE,
             WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
             EMERGENCY_TONE,
@@ -11307,6 +11324,8 @@
             VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
             VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
+            VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+                    WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index e0d085c..687aa83 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -17,19 +17,33 @@
 
 import android.Manifest;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.os.IBinder;
 import android.os.IStatsManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+
+/*
+ *
+ *
+ *
+ *
+ * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
+ * The new StatsManager is to be found in android.app.StatsManager.
+ * TODO: Delete this file!
+ *
+ *
+ *
+ *
+ */
+
+
 /**
  * API for StatsD clients to send configurations and retrieve data.
  *
  * @hide
  */
-@SystemApi
-public final class StatsManager {
+public class StatsManager {
     IStatsManager mService;
     private static final String TAG = "StatsManager";
 
@@ -55,7 +69,7 @@
      * Clients can send a configuration and simultaneously registers the name of a broadcast
      * receiver that listens for when it should request data.
      *
-     * @param configKey An arbitrary string that allows clients to track the configuration.
+     * @param configKey An arbitrary integer that allows clients to track the configuration.
      * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
      *                  dependencies eg, conditions and matchers).
      * @param pkg       The package name to receive the broadcast.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 5880c6a..ba21ccb 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -70,7 +70,7 @@
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
         long signingBlockSize =
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
-        long dataSize = apk.length() - signingBlockSize - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
+        long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
 
         ByteBuffer output = bufferFactory.create(
@@ -132,11 +132,13 @@
         }
 
         if (headerOutput != null) {
+            headerOutput.order(ByteOrder.LITTLE_ENDIAN);
             generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1,
                     DEFAULT_SALT);
         }
 
         if (extensionsOutput != null) {
+            extensionsOutput.order(ByteOrder.LITTLE_ENDIAN);
             generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset,
                     signingBlockSize, signatureInfo.eocdOffset);
         }
@@ -306,6 +308,14 @@
         return rootHash;
     }
 
+    private static void bufferPut(ByteBuffer buffer, byte value) {
+        // FIXME(b/72459251): buffer.put(value) does NOT work surprisingly. The position() after put
+        // does NOT even change. This hack workaround the problem, but the root cause remains
+        // unkonwn yet.  This seems only happen when it goes through the apk install flow on my
+        // setup.
+        buffer.put(new byte[] { value });
+    }
+
     private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
             byte[] salt) {
         if (salt.length != 8) {
@@ -315,22 +325,23 @@
         // TODO(b/30972906): update the reference when there is a better one in public.
         buffer.put("TrueBrew".getBytes());  // magic
 
-        buffer.put((byte) 1);        // major version
-        buffer.put((byte) 0);        // minor version
-        buffer.put((byte) 12);       // log2(block-size): log2(4096)
-        buffer.put((byte) 7);        // log2(leaves-per-node): log2(4096 / 32)
+        bufferPut(buffer, (byte) 1);        // major version
+        bufferPut(buffer, (byte) 0);        // minor version
+        bufferPut(buffer, (byte) 12);       // log2(block-size): log2(4096)
+        bufferPut(buffer, (byte) 7);        // log2(leaves-per-node): log2(4096 / 32)
 
-        buffer.putShort((short) 1);  // meta algorithm, SHA256_MODE == 1
-        buffer.putShort((short) 1);  // data algorithm, SHA256_MODE == 1
+        buffer.putShort((short) 1);         // meta algorithm, SHA256_MODE == 1
+        buffer.putShort((short) 1);         // data algorithm, SHA256_MODE == 1
 
-        buffer.putInt(0x1);          // flags, 0x1: has extension
-        buffer.putInt(0);            // reserved
+        buffer.putInt(0x1);                 // flags, 0x1: has extension
+        buffer.putInt(0);                   // reserved
 
-        buffer.putLong(fileSize);    // original file size
+        buffer.putLong(fileSize);           // original file size
 
-        buffer.put((byte) 0);        // auth block offset, disabled here
-        buffer.put(salt);            // salt (8 bytes)
-        // skip(buffer, 22);            // reserved
+        bufferPut(buffer, (byte) 0);        // auth block offset, disabled here
+        bufferPut(buffer, (byte) 2);        // extension count
+        buffer.put(salt);                   // salt (8 bytes)
+        // skip(buffer, 22);                // reserved
 
         buffer.rewind();
         return buffer;
@@ -340,11 +351,6 @@
             long signingBlockSize, long eocdOffset) {
         // Snapshot of the FSVerity structs (subject to change once upstreamed).
         //
-        // struct fsverity_header_extension {
-        //   u8 extension_count;
-        //   u8 reserved[7];
-        // };
-        //
         // struct fsverity_extension {
         //   __le16 length;
         //   u8 type;
@@ -363,10 +369,6 @@
         //   u8 databytes[];
         // };
 
-        // struct fsverity_header_extension
-        buffer.put((byte) 2);        // extension count
-        skip(buffer, 3);             // reserved
-
         final int kSizeOfFsverityExtensionHeader = 8;
 
         {
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
new file mode 100644
index 0000000..5607b11
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.app.ActivityManager;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.graphics.GraphicBuffer;
+
+/**
+ * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
+ * runner control certain aspects of the recents animation, and to notify window manager when the
+ * animation has completed.
+ *
+ * {@hide}
+ */
+interface IRecentsAnimationController {
+
+    /**
+     * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
+     * current set of task ids provided to the handler.
+     */
+    ActivityManager.TaskSnapshot screenshotTask(int taskId);
+
+    /**
+     * Notifies to the system that the animation into Recents should end, and all leashes associated
+     * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
+     * the home activity should be moved to the top. Otherwise, the home activity is hidden and the
+     * user is returned to the app.
+     */
+    void finish(boolean moveHomeToTop);
+
+    /**
+     * Called by the handler to indicate that the recents animation input consumer should be
+     * enabled. This is currently used to work around an issue where registering an input consumer
+     * mid-animation causes the existing motion event chain to be canceled. Instead, the caller
+     * may register the recents animation input consumer prior to starting the recents animation
+     * and then enable it mid-animation to start receiving touch events.
+     */
+    void setInputConsumerEnabled(boolean enabled);
+}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
new file mode 100644
index 0000000..ea6226b
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.RemoteAnimationTarget;
+import android.view.IRecentsAnimationController;
+
+/**
+ * Interface that is used to callback from window manager to the process that runs a recents
+ * animation to start or cancel it.
+ *
+ * {@hide}
+ */
+oneway interface IRecentsAnimationRunner {
+
+    /**
+     * Called when the system is ready for the handler to start animating all the visible tasks.
+     */
+    void onAnimationStart(in IRecentsAnimationController controller,
+            in RemoteAnimationTarget[] apps);
+
+    /**
+     * Called when the system needs to cancel the current animation. This can be due to the
+     * wallpaper not drawing in time, or the handler not finishing the animation within a predefined
+     * amount of time.
+     */
+    void onAnimationCanceled();
+}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 17b6ddc..d7fd329 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -157,10 +157,8 @@
      * @param flags See {@code View#startDragAndDrop}
      * @param surface Surface containing drag shadow image
      * @param touchSource See {@code InputDevice#getSource()}
-     * @param touchX TODO (b/72072998): Fix the issue that the system server misuse the arguments as
-     *         initial touch point while the framework passes drag shadow size.
-     * @param touchY TODO (b/72072998): Fix the issue that the system server misuse the arguments as
-     *         initial touch point while the framework passes drag shadow size.
+     * @param touchX X coordinate of last touch point
+     * @param touchY Y coordinate of last touch point
      * @param thumbCenterX X coordinate for the position within the shadow image that should be
      *         underneath the touch point during the drag and drop operation.
      * @param thumbCenterY Y coordinate for the position within the shadow image that should be
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index f39e618..c28c389 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.app.WindowConfiguration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Parcel;
@@ -98,8 +99,14 @@
      */
     public final Rect sourceContainerBounds;
 
+    /**
+     * The window configuration for the target.
+     */
+    public final WindowConfiguration windowConfiguration;
+
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
-            Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
+            Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
+            WindowConfiguration windowConfig) {
         this.mode = mode;
         this.taskId = taskId;
         this.leash = leash;
@@ -108,6 +115,7 @@
         this.prefixOrderIndex = prefixOrderIndex;
         this.position = new Point(position);
         this.sourceContainerBounds = new Rect(sourceContainerBounds);
+        this.windowConfiguration = windowConfig;
     }
 
     public RemoteAnimationTarget(Parcel in) {
@@ -119,6 +127,7 @@
         prefixOrderIndex = in.readInt();
         position = in.readParcelable(null);
         sourceContainerBounds = in.readParcelable(null);
+        windowConfiguration = in.readParcelable(null);
     }
 
     @Override
@@ -136,6 +145,7 @@
         dest.writeInt(prefixOrderIndex);
         dest.writeParcelable(position, 0 /* flags */);
         dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+        dest.writeParcelable(windowConfiguration, 0 /* flags */);
     }
 
     public static final Creator<RemoteAnimationTarget> CREATOR
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a2ecfc4..3d6a6fe 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+
 import static java.lang.Math.max;
 
 import android.animation.AnimatorInflater;
@@ -8548,6 +8550,12 @@
         info.setLongClickable(isLongClickable());
         info.setContextClickable(isContextClickable());
         info.setLiveRegion(getAccessibilityLiveRegion());
+        if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipText != null)) {
+            info.setTooltipText(mTooltipInfo.mTooltipText);
+            info.addAction((mTooltipInfo.mTooltipPopup == null)
+                    ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP
+                    : AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP);
+        }
 
         // TODO: These make sense only if we are in an AdapterView but all
         // views can be selected. Maybe from accessibility perspective
@@ -8951,8 +8959,7 @@
             return;
         }
         mAccessibilityTraversalBeforeId = beforeId;
-        notifyAccessibilityStateChanged(
-                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -8995,8 +9002,7 @@
             return;
         }
         mAccessibilityTraversalAfterId = afterId;
-        notifyAccessibilityStateChanged(
-                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -9038,8 +9044,7 @@
                 && mID == View.NO_ID) {
             mID = generateViewId();
         }
-        notifyAccessibilityStateChanged(
-                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -10539,7 +10544,7 @@
 
         if (pflags3 != mPrivateFlags3) {
             mPrivateFlags3 = pflags3;
-            notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -11253,6 +11258,8 @@
 
         if (!isLayoutValid()) {
             mPrivateFlags |= PFLAG_WANTS_FOCUS;
+        } else {
+            clearParentsWantFocus();
         }
 
         handleFocusGainInternal(direction, previouslyFocusedRect);
@@ -11367,8 +11374,7 @@
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
             mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
                     & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
-            notifyAccessibilityStateChanged(
-                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -11427,8 +11433,7 @@
             if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
                 notifyAccessibilitySubtreeChanged();
             } else {
-                notifyAccessibilityStateChanged(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -11838,8 +11843,7 @@
                         || getAccessibilitySelectionEnd() != end)
                         && (start == end)) {
                     setAccessibilitySelection(start, end);
-                    notifyAccessibilityStateChanged(
-                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
                     return true;
                 }
             } break;
@@ -11856,6 +11860,21 @@
                     return true;
                 }
             } break;
+            case R.id.accessibilityActionShowTooltip: {
+                if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipPopup != null)) {
+                    // Tooltip already showing
+                    return false;
+                }
+                return showLongClickTooltip(0, 0);
+            }
+            case R.id.accessibilityActionHideTooltip: {
+                if ((mTooltipInfo == null) || (mTooltipInfo.mTooltipPopup == null)) {
+                    // No tooltip showing
+                    return false;
+                }
+                hideTooltip();
+                return true;
+            }
         }
         return false;
     }
@@ -13889,12 +13908,10 @@
                 if (oldIncludeForAccessibility != includeForAccessibility()) {
                     notifyAccessibilitySubtreeChanged();
                 } else {
-                    notifyAccessibilityStateChanged(
-                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
                 }
             } else if ((changed & ENABLED_MASK) != 0) {
-                notifyAccessibilityStateChanged(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -21854,8 +21871,7 @@
             if (selected) {
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
             } else {
-                notifyAccessibilityStateChanged(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -27057,6 +27073,8 @@
         final boolean fromTouch = (mPrivateFlags3 & PFLAG3_FINGER_DOWN) == PFLAG3_FINGER_DOWN;
         mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText);
         mAttachInfo.mTooltipHost = this;
+        // The available accessibility actions have changed
+        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
         return true;
     }
 
@@ -27075,6 +27093,8 @@
         if (mAttachInfo != null) {
             mAttachInfo.mTooltipHost = null;
         }
+        // The available accessibility actions have changed
+        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     private boolean showLongClickTooltip(int x, int y) {
@@ -27083,8 +27103,8 @@
         return showTooltip(x, y, true);
     }
 
-    private void showHoverTooltip() {
-        showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false);
+    private boolean showHoverTooltip() {
+        return showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false);
     }
 
     boolean dispatchTooltipHoverEvent(MotionEvent event) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3bb3a4c..1c5e871 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -98,11 +98,13 @@
     int DOCKED_BOTTOM = 4;
 
     /** @hide */
-    final static String INPUT_CONSUMER_PIP = "pip_input_consumer";
+    String INPUT_CONSUMER_PIP = "pip_input_consumer";
     /** @hide */
-    final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
+    String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
     /** @hide */
-    final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+    String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+    /** @hide */
+    String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer";
 
     /**
      * Not set up for a transition.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3ee282e..23e7d61 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -727,6 +727,7 @@
     private CharSequence mError;
     private CharSequence mPaneTitle;
     private CharSequence mContentDescription;
+    private CharSequence mTooltipText;
     private String mViewIdResourceName;
     private ArrayList<String> mExtraDataKeys;
 
@@ -2655,6 +2656,34 @@
     }
 
     /**
+     * Gets the tooltip text of this node.
+     *
+     * @return The tooltip text.
+     */
+    @Nullable
+    public CharSequence getTooltipText() {
+        return mTooltipText;
+    }
+
+    /**
+     * Sets the tooltip text of this node.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param tooltipText The tooltip text.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setTooltipText(@Nullable CharSequence tooltipText) {
+        enforceNotSealed();
+        mTooltipText = (tooltipText == null) ? null
+                : tooltipText.subSequence(0, tooltipText.length());
+    }
+
+    /**
      * Sets the view for which the view represented by this info serves as a
      * label for accessibility purposes.
      *
@@ -3209,6 +3238,10 @@
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
+        if (!Objects.equals(mTooltipText, DEFAULT.mTooltipText)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
         if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
@@ -3329,6 +3362,8 @@
             parcel.writeCharSequence(mContentDescription);
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
+        if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
+
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
@@ -3401,6 +3436,7 @@
         mError = other.mError;
         mContentDescription = other.mContentDescription;
         mPaneTitle = other.mPaneTitle;
+        mTooltipText = other.mTooltipText;
         mViewIdResourceName = other.mViewIdResourceName;
 
         if (mActions != null) mActions.clear();
@@ -3522,6 +3558,7 @@
             mContentDescription = parcel.readCharSequence();
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+        if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt();
@@ -3684,6 +3721,10 @@
                 return "ACTION_SET_PROGRESS";
             case R.id.accessibilityActionContextClick:
                 return "ACTION_CONTEXT_CLICK";
+            case R.id.accessibilityActionShowTooltip:
+                return "ACTION_SHOW_TOOLTIP";
+            case R.id.accessibilityActionHideTooltip:
+                return "ACTION_HIDE_TOOLTIP";
             default:
                 return "ACTION_UNKNOWN";
         }
@@ -3797,6 +3838,7 @@
         builder.append("; error: ").append(mError);
         builder.append("; maxTextLength: ").append(mMaxTextLength);
         builder.append("; contentDescription: ").append(mContentDescription);
+        builder.append("; tooltipText: ").append(mTooltipText);
         builder.append("; viewIdResName: ").append(mViewIdResourceName);
 
         builder.append("; checkable: ").append(isCheckable());
@@ -4211,6 +4253,20 @@
         public static final AccessibilityAction ACTION_MOVE_WINDOW =
                 new AccessibilityAction(R.id.accessibilityActionMoveWindow);
 
+        /**
+         * Action to show a tooltip. A node should expose this action only for views with tooltip
+         * text that but are not currently showing a tooltip.
+         */
+        public static final AccessibilityAction ACTION_SHOW_TOOLTIP =
+                new AccessibilityAction(R.id.accessibilityActionShowTooltip);
+
+        /**
+         * Action to hide a tooltip. A node should expose this action only for views that are
+         * currently showing a tooltip.
+         */
+        public static final AccessibilityAction ACTION_HIDE_TOOLTIP =
+                new AccessibilityAction(R.id.accessibilityActionHideTooltip);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index e554540..eba9176 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -21,6 +21,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
@@ -898,4 +899,37 @@
      */
     boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
             @Nullable Bundle opts);
+
+    /**
+     * Called by the input method to tell a hint about the locales of text to be committed.
+     *
+     * <p>This is just a hint for editor authors (and the system) to choose better options when
+     * they have to disambiguate languages, like editor authors can do for input methods with
+     * {@link EditorInfo#hintLocales}.</p>
+     *
+     * <p>The language hint provided by this callback should have higher priority than
+     * {@link InputMethodSubtype#getLanguageTag()}, which cannot be updated dynamically.</p>
+     *
+     * <p>Note that in general it is discouraged for input method to specify
+     * {@link android.text.style.LocaleSpan} when inputting text, mainly because of application
+     * compatibility concerns.</p>
+     * <ul>
+     *     <li>When an existing text that already has {@link android.text.style.LocaleSpan} is being
+     *     modified by both the input method and application, there is no reliable and easy way to
+     *     keep track of who modified {@link android.text.style.LocaleSpan}. For instance, if the
+     *     text was updated by JavaScript, it it highly likely that span information is completely
+     *     removed, while some input method attempts to preserve spans if possible.</li>
+     *     <li>There is no clear semantics regarding whether {@link android.text.style.LocaleSpan}
+     *     means a weak (ignorable) hint or a strong hint. This becomes more problematic when
+     *     multiple {@link android.text.style.LocaleSpan} instances are specified to the same
+     *     text region, especially when those spans are conflicting.</li>
+     * </ul>
+     * @param languageHint list of languages sorted by the priority and/or probability
+     */
+    default void reportLanguageHint(@NonNull LocaleList languageHint) {
+        // Intentionally empty.
+        //
+        // We need to have *some* default implementation for the source compatibility.
+        // See Bug 72127682 for details.
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index f671e22..cbe6856 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,8 +16,10 @@
 
 package android.view.inputmethod;
 
+import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.view.KeyEvent;
 
 /**
@@ -303,4 +305,13 @@
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
         return mTarget.commitContent(inputContentInfo, flags, opts);
     }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
+    public void reportLanguageHint(@NonNull LocaleList languageHint) {
+        mTarget.reportLanguageHint(languageHint);
+    }
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3f0d006..594d240 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.StrictMode;
@@ -6035,6 +6036,11 @@
         public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
             return getTarget().commitContent(inputContentInfo, flags, opts);
         }
+
+        @Override
+        public void reportLanguageHint(@NonNull LocaleList languageHint) {
+            getTarget().reportLanguageHint(languageHint);
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 28291ae..e08caa8 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -64,6 +65,7 @@
     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
     private static final int DO_CLOSE_CONNECTION = 150;
     private static final int DO_COMMIT_CONTENT = 160;
+    private static final int DO_REPORT_LANGUAGE_HINT = 170;
 
     @GuardedBy("mLock")
     @Nullable
@@ -217,6 +219,10 @@
                 callback));
     }
 
+    public void reportLanguageHint(@NonNull LocaleList languageHint) {
+        dispatchMessage(obtainMessageO(DO_REPORT_LANGUAGE_HINT, languageHint));
+    }
+
     void dispatchMessage(Message msg) {
         // If we are calling this from the main thread, then we can call
         // right through.  Otherwise, we need to send the message to the
@@ -577,6 +583,16 @@
                 }
                 return;
             }
+            case DO_REPORT_LANGUAGE_HINT: {
+                final LocaleList languageHint = (LocaleList) msg.obj;
+                final InputConnection ic = getInputConnection();
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "reportLanguageHint on inactive InputConnection");
+                    return;
+                }
+                ic.reportLanguageHint(languageHint);
+                return;
+            }
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index c227991..e69a87ff 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.view;
 
 import android.os.Bundle;
+import android.os.LocaleList;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
@@ -78,4 +79,6 @@
 
     void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts, int sec,
             IInputContextCallback callback);
+
+    void reportLanguageHint(in LocaleList languageHint);
 }
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 5b65bbe..34be598 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -22,6 +22,7 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -620,6 +621,14 @@
     }
 
     @AnyThread
+    public void reportLanguageHint(@NonNull LocaleList languageHint) {
+        try {
+            mIInputContext.reportLanguageHint(languageHint);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @AnyThread
     private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
         return (mMissingMethods & methodFlag) == methodFlag;
     }
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 8b3ce66..262b553 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -72,7 +72,6 @@
     }
 
     sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(animatedImg));
-    drawable->start();
     return reinterpret_cast<jlong>(drawable.release());
 }
 
@@ -114,9 +113,9 @@
     return drawable->isRunning();
 }
 
-static void AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    drawable->start();
+    return drawable->start();
 }
 
 static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -138,7 +137,7 @@
     { "nGetAlpha",           "(J)I",                                                         (void*) AnimatedImageDrawable_nGetAlpha },
     { "nSetColorFilter",     "(JJ)V",                                                        (void*) AnimatedImageDrawable_nSetColorFilter },
     { "nIsRunning",          "(J)Z",                                                         (void*) AnimatedImageDrawable_nIsRunning },
-    { "nStart",              "(J)V",                                                         (void*) AnimatedImageDrawable_nStart },
+    { "nStart",              "(J)Z",                                                         (void*) AnimatedImageDrawable_nStart },
     { "nStop",               "(J)V",                                                         (void*) AnimatedImageDrawable_nStop },
     { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
 };
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea791a5..0861e710 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -971,6 +971,23 @@
                 android:description="@string/permdesc_manageOwnCalls"
                 android:protectionLevel="normal" />
 
+    <!-- Allows a calling app to continue a call which was started in another app.  An example is a
+         video calling app that wants to continue a voice call on the user's mobile network.<p>
+         When the handover of a call from one app to another takes place, there are two devices
+         which are involved in the handover; the initiating and receiving devices.  The initiating
+         device is where the request to handover the call was started, and the receiving device is
+         where the handover request is confirmed by the other party.<p>
+         This permission protects access to the
+         {@link android.telecom.TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} which
+         the receiving side of the handover uses to accept a handover.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.ACCEPT_HANDOVER"
+                android:permissionGroup="android.permission-group.PHONE"
+                android.label="@string/permlab_acceptHandover"
+                android:description="@string/permdesc_acceptHandovers"
+                android:protectionLevel="dangerous" />
+
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device microphone                        -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index b40117e..c90a0df 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -163,4 +163,9 @@
   <!-- Action used to manually trigger an autofill request -->
   <item type="id" name="autofill" />
 
+    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_TOOLTIP}. -->
+    <item type="id" name="accessibilityActionShowTooltip" />
+
+    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. -->
+    <item type="id" name="accessibilityActionHideTooltip" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9cdf553..80fc5db 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2874,6 +2874,8 @@
     </public-group>
 
     <public-group type="id" first-id="0x01020044">
+      <public name="accessibilityActionShowTooltip" />
+      <public name="accessibilityActionHideTooltip" />
     </public-group>
 
     <public-group type="string" first-id="0x0104001b">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 71e963a..327b33b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1133,6 +1133,17 @@
     <string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
         order to improve the calling experience.</string>
 
+    <!-- Title of an application permission.  When granted the user is giving access to a third
+         party app to continue a call which originated in another app.  For example, the user
+         could be in a voice call over their carrier's mobile network, and a third party video
+         calling app wants to continue that voice call as a video call. -->
+    <string name="permlab_acceptHandover">continue a call from another app</string>
+    <!-- Description of an application permission.  When granted the user is giving access to a
+         third party app to continue a call which originated in another app.  For example, the user
+         could be in a voice call over their carrier's mobile network, and a third party video
+         calling app wants to continue that voice call as a video call -->
+    <string name="permdesc_acceptHandovers">Allows the app to continue a call which was started in another app.</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_readPhoneNumbers">read phone numbers</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3027,6 +3038,8 @@
 
     <!-- Notification title for a nearby open wireless network.-->
     <string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
+    <!-- Notification title for a nearby carrier wireless network.-->
+    <string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
     <!-- Notification title when the system is connecting to the specified open network. The network name is specified in the notification content. -->
     <string name="wifi_available_title_connecting">Connecting to open Wi\u2011Fi network</string>
     <!-- Notification title when the system has connected to the open network. The network name is specified in the notification content. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ee20873..7938f16 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1912,6 +1912,7 @@
   <java-symbol type="plurals" name="wifi_available" />
   <java-symbol type="plurals" name="wifi_available_detailed" />
   <java-symbol type="string" name="wifi_available_title" />
+  <java-symbol type="string" name="wifi_available_carrier_network_title" />
   <java-symbol type="string" name="wifi_available_title_connecting" />
   <java-symbol type="string" name="wifi_available_title_connected" />
   <java-symbol type="string" name="wifi_available_title_failed_to_connect" />
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 6d3ddd5..3034a10 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -166,8 +166,7 @@
 
     @Override
     public void start() {
-        if (isRunning() == false) {
-            nStart(mNativePtr);
+        if (nStart(mNativePtr)) {
             invalidateSelf();
         }
     }
@@ -186,7 +185,7 @@
     private static native int nGetAlpha(long nativePtr);
     private static native void nSetColorFilter(long nativePtr, long nativeFilter);
     private static native boolean nIsRunning(long nativePtr);
-    private static native void nStart(long nativePtr);
+    private static native boolean nStart(long nativePtr);
     private static native void nStop(long nativePtr);
     private static native long nNativeByteSize(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3..7ad062a 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -163,7 +163,7 @@
     /**
      * Create a drawable by opening a given file path and decoding the bitmap.
      */
-    @SuppressWarnings("unused")
+    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, String filepath) {
         this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
         mBitmapState.mTargetDensity = mTargetDensity;
@@ -188,7 +188,7 @@
     /**
      * Create a drawable by decoding a bitmap from the given input stream.
      */
-    @SuppressWarnings("unused")
+    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, java.io.InputStream is) {
         this(new BitmapState(BitmapFactory.decodeStream(is)), null);
         mBitmapState.mTargetDensity = mTargetDensity;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 36dd06f..e01bf3d 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -36,18 +36,27 @@
     mColorFilter = mStagingColorFilter;
 }
 
-void AnimatedImageDrawable::start() {
+bool AnimatedImageDrawable::start() {
     SkAutoExclusive lock(mLock);
+    if (mSkAnimatedImage->isRunning()) {
+        return false;
+    }
 
-    mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
+    if (!mSnapshot) {
+        mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot());
+    }
 
+    // While stopped, update() does not decode, but it does advance the time.
+    // This prevents us from skipping ahead when we resume.
+    const double currentTime = SkTime::GetMSecs();
+    mSkAnimatedImage->update(currentTime);
     mSkAnimatedImage->start();
+    return mSkAnimatedImage->isRunning();
 }
 
 void AnimatedImageDrawable::stop() {
     SkAutoExclusive lock(mLock);
     mSkAnimatedImage->stop();
-    mSnapshot.reset(nullptr);
 }
 
 bool AnimatedImageDrawable::isRunning() {
@@ -120,7 +129,7 @@
     }
 
     SkAutoExclusive lock(mLock);
-    if (mSkAnimatedImage->isRunning()) {
+    if (mSnapshot) {
         canvas->drawPicture(mSnapshot, nullptr, lazyPaint.getMaybeNull());
     } else {
         // TODO: we could potentially keep the cached surface around if there is a paint and we know
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 18764af..1ebb585 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -58,7 +58,8 @@
 
     double drawStaging(SkCanvas* canvas);
 
-    void start();
+    // Returns true if the animation was started; false otherwise (e.g. it was already running)
+    bool start();
     void stop();
     bool isRunning();
 
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index e5e865f..3d57fbd 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -19,6 +19,7 @@
     srcs: [
         ":IDropBoxManagerService.aidl",
         "src/os/DropBoxManager.cpp",
+        "src/os/StatsDimensionsValue.cpp",
         "src/os/StatsLogEventWrapper.cpp",
     ],
 
diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h
new file mode 100644
index 0000000..cc0b056
--- /dev/null
+++ b/libs/services/include/android/os/StatsDimensionsValue.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+#ifndef STATS_DIMENSIONS_VALUE_H
+#define STATS_DIMENSIONS_VALUE_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/String16.h>
+#include <vector>
+
+namespace android {
+namespace os {
+
+// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java.
+class StatsDimensionsValue : public android::Parcelable {
+public:
+    StatsDimensionsValue();
+
+    StatsDimensionsValue(int32_t field, String16 value);
+    StatsDimensionsValue(int32_t field, int32_t value);
+    StatsDimensionsValue(int32_t field, int64_t value);
+    StatsDimensionsValue(int32_t field, bool value);
+    StatsDimensionsValue(int32_t field, float value);
+    StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value);
+
+    virtual ~StatsDimensionsValue();
+
+    virtual android::status_t writeToParcel(android::Parcel* out) const override;
+    virtual android::status_t readFromParcel(const android::Parcel* in) override;
+
+private:
+    // Keep constants in sync with android/os/StatsDimensionsValue.java
+    // and stats_log.proto's DimensionValue.
+    static const int kStrValueType = 2;
+    static const int kIntValueType = 3;
+    static const int kLongValueType = 4;
+    static const int kBoolValueType = 5;
+    static const int kFloatValueType = 6;
+    static const int kTupleValueType = 7;
+
+    int32_t mField;
+    int32_t mValueType;
+
+    // This isn't very clever, but it isn't used for long-term storage, so it'll do.
+    String16 mStrValue;
+    int32_t mIntValue;
+    int64_t mLongValue;
+    bool mBoolValue;
+    float mFloatValue;
+    std::vector<StatsDimensionsValue> mTupleValue;
+};
+
+}  // namespace os
+}  // namespace android
+
+#endif // STATS_DIMENSIONS_VALUE_H
diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp
new file mode 100644
index 0000000..0052e0b
--- /dev/null
+++ b/libs/services/src/os/StatsDimensionsValue.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "StatsDimensionsValue"
+
+#include "android/os/StatsDimensionsValue.h"
+
+#include <cutils/log.h>
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+using std::vector;
+
+namespace android {
+namespace os {
+
+StatsDimensionsValue::StatsDimensionsValue() {};
+
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) :
+    mField(field),
+    mValueType(kStrValueType),
+    mStrValue(value) {
+}
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) :
+    mField(field),
+    mValueType(kIntValueType),
+    mIntValue(value) {
+}
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) :
+    mField(field),
+    mValueType(kLongValueType),
+    mLongValue(value) {
+}
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) :
+    mField(field),
+    mValueType(kBoolValueType),
+    mBoolValue(value) {
+}
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) :
+    mField(field),
+    mValueType(kFloatValueType),
+    mFloatValue(value) {
+}
+StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) :
+    mField(field),
+    mValueType(kTupleValueType),
+    mTupleValue(value) {
+}
+
+StatsDimensionsValue::~StatsDimensionsValue() {}
+
+status_t
+StatsDimensionsValue::writeToParcel(Parcel* out) const {
+    status_t err ;
+
+    err = out->writeInt32(mField);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = out->writeInt32(mValueType);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    switch (mValueType) {
+        case kStrValueType:
+            err = out->writeString16(mStrValue);
+            break;
+        case kIntValueType:
+            err = out->writeInt32(mIntValue);
+            break;
+        case kLongValueType:
+            err = out->writeInt64(mLongValue);
+            break;
+        case kBoolValueType:
+            err = out->writeBool(mBoolValue);
+            break;
+        case kFloatValueType:
+            err = out->writeFloat(mFloatValue);
+            break;
+        case kTupleValueType:
+            {
+                int sz = mTupleValue.size();
+                err = out->writeInt32(sz);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+                for (int i = 0; i < sz; ++i) {
+                    err = mTupleValue[i].writeToParcel(out);
+                    if (err != NO_ERROR) {
+                        return err;
+                    }
+                }
+            }
+            break;
+        default:
+            err = UNKNOWN_ERROR;
+            break;
+    }
+    return err;
+}
+
+status_t
+StatsDimensionsValue::readFromParcel(const Parcel* in)
+{
+    // Implement me if desired. We don't currently use this.
+    ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented.");
+    (void)in; // To prevent compile error of unused parameter 'in'
+    return UNKNOWN_ERROR;
+}
+
+}  // namespace os
+}  // namespace android
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f41e33f..44d9099 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2639,7 +2639,8 @@
         /**
          * Returns the supported range of quality values.
          *
-         * @hide
+         * Quality is implementation-specific. As a general rule, a higher quality
+         * setting results in a better image quality and a lower compression ratio.
          */
         public Range<Integer> getQualityRange() {
             return mQualityRange;
@@ -2751,7 +2752,7 @@
             }
             if (info.containsKey("feature-bitrate-modes")) {
                 for (String mode: info.getString("feature-bitrate-modes").split(",")) {
-                    mBitControl |= parseBitrateMode(mode);
+                    mBitControl |= (1 << parseBitrateMode(mode));
                 }
             }
 
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index c6496eb..e9128e4 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -601,8 +601,6 @@
      * codec specific, but lower values generally result in more efficient
      * (smaller-sized) encoding.
      *
-     * @hide
-     *
      * @see MediaCodecInfo.EncoderCapabilities#getQualityRange()
      */
     public static final String KEY_QUALITY = "quality";
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/fingerprint_dialog.xml
index e5f62b3..161f13f 100644
--- a/packages/SystemUI/res/layout/fingerprint_dialog.xml
+++ b/packages/SystemUI/res/layout/fingerprint_dialog.xml
@@ -99,6 +99,7 @@
             android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
             android:layout_gravity="center_horizontal"
             android:scaleType="centerInside"
+            android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon"
             android:src="@drawable/fingerprint_icon"/>
 
         <TextView
@@ -112,6 +113,8 @@
             android:textSize="12sp"
             android:visibility="invisible"
             android:gravity="center_horizontal"
+            android:accessibilityLiveRegion="polite"
+            android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
             android:textColor="@color/fingerprint_error_message_color"/>
 
         <LinearLayout android:id="@+id/buttonPanel"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8ea1225..209d444 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -259,6 +259,13 @@
     <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
     <string name="cancel">Cancel</string>
 
+    <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
+    <!-- Content description of the application icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_fingerprint_dialog_app_icon">Application icon</string>
+    <!-- Content description for the error/help message are when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_fingerprint_dialog_help_area">Help message area</string>
+
     <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index cc4bc58..da50776 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -29,4 +29,9 @@
      */
     GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
             int maxLayer, boolean useIdentityTransform, int rotation);
+
+    /**
+     * Called when the overview service has started the recents animation.
+     */
+    void onRecentsAnimationStarted();
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c9a6ea9..f9e1069 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -32,6 +32,7 @@
 import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration.ActivityType;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -48,7 +49,10 @@
 import android.os.UserHandle;
 import android.util.IconDrawableFactory;
 import android.util.Log;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
 
+import android.view.RemoteAnimationTarget;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -243,10 +247,9 @@
     /**
      * Starts the recents activity. The caller should manage the thread on which this is called.
      */
-    public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options,
-            ActivityOptions opts, int userId, Consumer<Boolean> resultCallback,
+    public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver,
+            RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
-        Bundle activityOptions = opts != null ? opts.toBundle() : null;
         try {
             IAssistDataReceiver receiver = null;
             if (assistDataReceiver != null) {
@@ -259,8 +262,24 @@
                     }
                 };
             }
-            ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions,
-                    userId);
+            IRecentsAnimationRunner runner = null;
+            if (animationHandler != null) {
+                runner = new IRecentsAnimationRunner.Stub() {
+                    public void onAnimationStart(IRecentsAnimationController controller,
+                            RemoteAnimationTarget[] apps) {
+                        final RecentsAnimationControllerCompat controllerCompat =
+                                new RecentsAnimationControllerCompat(controller);
+                        final RemoteAnimationTargetCompat[] appsCompat =
+                                RemoteAnimationTargetCompat.wrap(apps);
+                        animationHandler.onAnimationStart(controllerCompat, appsCompat);
+                    }
+
+                    public void onAnimationCanceled() {
+                        animationHandler.onAnimationCanceled();
+                    }
+                };
+            }
+            ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
             if (resultCallback != null) {
                 resultCallbackHandler.post(new Runnable() {
                     @Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
similarity index 80%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
index cd943f6..7cd6c51 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
@@ -22,7 +22,7 @@
 /**
  * Abstract class for assist data receivers.
  */
-public abstract class AssistDataReceiverCompat {
-    public abstract void onHandleAssistData(Bundle resultData);
-    public abstract void onHandleAssistScreenshot(Bitmap screenshot);
+public abstract class AssistDataReceiver {
+    public void onHandleAssistData(Bundle resultData) {}
+    public void onHandleAssistScreenshot(Bitmap screenshot) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index db4f988..38b8ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.systemui.shared.system;
 
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 
 import android.os.Binder;
 import android.os.IBinder;
@@ -29,11 +30,12 @@
 import android.view.InputEvent;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
+import android.view.WindowManagerGlobal;
 
 import java.io.PrintWriter;
 
 /**
- * Manages the input consumer that allows the SystemUI to control the PiP.
+ * Manages the input consumer that allows the SystemUI to directly receive touch input.
  */
 public class InputConsumerController {
 
@@ -55,12 +57,12 @@
     }
 
     /**
-     * Input handler used for the PiP input consumer. Input events are batched and consumed with the
+     * Input handler used for the input consumer. Input events are batched and consumed with the
      * SurfaceFlinger vsync.
      */
-    private final class PipInputEventReceiver extends BatchedInputEventReceiver {
+    private final class InputEventReceiver extends BatchedInputEventReceiver {
 
-        public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
+        public InputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper, Choreographer.getSfInstance());
         }
 
@@ -68,7 +70,6 @@
         public void onInputEvent(InputEvent event, int displayId) {
             boolean handled = true;
             try {
-                // To be implemented for input handling over Pip windows
                 if (mListener != null && event instanceof MotionEvent) {
                     MotionEvent ev = (MotionEvent) event;
                     handled = mListener.onTouchEvent(ev);
@@ -81,15 +82,35 @@
 
     private final IWindowManager mWindowManager;
     private final IBinder mToken;
+    private final String mName;
 
-    private PipInputEventReceiver mInputEventReceiver;
+    private InputEventReceiver mInputEventReceiver;
     private TouchListener mListener;
     private RegistrationListener mRegistrationListener;
 
-    public InputConsumerController(IWindowManager windowManager) {
+    /**
+     * @param name the name corresponding to the input consumer that is defined in the system.
+     */
+    public InputConsumerController(IWindowManager windowManager, String name) {
         mWindowManager = windowManager;
         mToken = new Binder();
-        registerInputConsumer();
+        mName = name;
+    }
+
+    /**
+     * @return A controller for the pip input consumer.
+     */
+    public static InputConsumerController getPipInputConsumer() {
+        return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_PIP);
+    }
+
+    /**
+     * @return A controller for the recents animation input consumer.
+     */
+    public static InputConsumerController getRecentsAnimationInputConsumer() {
+        return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_RECENTS_ANIMATION);
     }
 
     /**
@@ -125,12 +146,12 @@
         if (mInputEventReceiver == null) {
             final InputChannel inputChannel = new InputChannel();
             try {
-                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
-                mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel);
+                mWindowManager.destroyInputConsumer(mName);
+                mWindowManager.createInputConsumer(mToken, mName, inputChannel);
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to create PIP input consumer", e);
+                Log.e(TAG, "Failed to create input consumer", e);
             }
-            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+            mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
             if (mRegistrationListener != null) {
                 mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
             }
@@ -143,9 +164,9 @@
     public void unregisterInputConsumer() {
         if (mInputEventReceiver != null) {
             try {
-                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+                mWindowManager.destroyInputConsumer(mName);
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to destroy PIP input consumer", e);
+                Log.e(TAG, "Failed to destroy input consumer", e);
             }
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
new file mode 100644
index 0000000..9a7abf8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRecentsAnimationController;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+public class RecentsAnimationControllerCompat {
+
+    private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName();
+
+    private IRecentsAnimationController mAnimationController;
+
+    public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
+        mAnimationController = animationController;
+    }
+
+    public ThumbnailData screenshotTask(int taskId) {
+        try {
+            TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
+            return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to screenshot task", e);
+            return new ThumbnailData();
+        }
+    }
+
+    public void setInputConsumerEnabled(boolean enabled) {
+        try {
+            mAnimationController.setInputConsumerEnabled(enabled);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set input consumer enabled state", e);
+        }
+    }
+
+    public void finish(boolean toHome) {
+        try {
+            mAnimationController.finish(toHome);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to finish recents animation", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
similarity index 60%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index cd943f6..bf6179d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -16,13 +16,16 @@
 
 package com.android.systemui.shared.system;
 
-import android.graphics.Bitmap;
-import android.os.Bundle;
+public interface RecentsAnimationListener {
 
-/**
- * Abstract class for assist data receivers.
- */
-public abstract class AssistDataReceiverCompat {
-    public abstract void onHandleAssistData(Bundle resultData);
-    public abstract void onHandleAssistScreenshot(Bitmap screenshot);
-}
+    /**
+     * Called when the animation into Recents can start. This call is made on the binder thread.
+     */
+    void onAnimationStart(RecentsAnimationControllerCompat controller,
+            RemoteAnimationTargetCompat[] apps);
+
+    /**
+     * Called when the animation into Recents was canceled. This call is made on the binder thread.
+     */
+    void onAnimationCanceled();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 244c1b9..b6e49ae 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -77,6 +77,15 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        public void onRecentsAnimationStarted() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                notifyRecentsAnimationStarted();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -214,6 +223,12 @@
         }
     }
 
+    private void notifyRecentsAnimationStarted() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onRecentsAnimationStarted();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(TAG_OPS + " state:");
@@ -224,6 +239,7 @@
     }
 
     public interface OverviewProxyListener {
-        void onConnectionChanged(boolean isConnected);
+        default void onConnectionChanged(boolean isConnected) {}
+        default void onRecentsAnimationStarted() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index 19bc2ec..9779937 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -146,7 +146,7 @@
         subtitle.setText(mBundle.getCharSequence(FingerprintDialog.KEY_SUBTITLE));
         description.setText(mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION));
         negative.setText(mBundle.getCharSequence(FingerprintDialog.KEY_NEGATIVE_TEXT));
-        image.setImageDrawable(getAppIcon());
+        setAppIcon(image);
 
         final CharSequence positiveText =
                 mBundle.getCharSequence(FingerprintDialog.KEY_POSITIVE_TEXT);
@@ -190,6 +190,7 @@
     private void showMessage(String message) {
         mHandler.removeMessages(FingerprintDialogImpl.MSG_CLEAR_MESSAGE);
         mErrorText.setText(message);
+        mErrorText.setContentDescription(message);
         mErrorText.setVisibility(View.VISIBLE);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
                 FingerprintDialog.HIDE_DIALOG_DELAY);
@@ -205,12 +206,16 @@
                 false /* userCanceled */), FingerprintDialog.HIDE_DIALOG_DELAY);
     }
 
-    private Drawable getAppIcon() {
+    private void setAppIcon(ImageView image) {
         final ActivityManager.RunningTaskInfo taskInfo = mActivityManagerWrapper.getRunningTask();
         final ComponentName cn = taskInfo.topActivity;
         final int userId = mActivityManagerWrapper.getCurrentUserId();
         final ActivityInfo activityInfo = mPackageManageWrapper.getActivityInfo(cn, userId);
-        return mActivityManagerWrapper.getBadgedActivityIcon(activityInfo, userId);
+        image.setImageDrawable(mActivityManagerWrapper.getBadgedActivityIcon(activityInfo, userId));
+        image.setContentDescription(
+                getResources().getString(R.string.accessibility_fingerprint_dialog_app_icon)
+                        + " "
+                        + mActivityManagerWrapper.getBadgedActivityLabel(activityInfo, userId));
     }
 
     public WindowManager.LayoutParams getLayoutParams() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 36531bb..24d0126 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,12 +16,10 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -43,6 +41,7 @@
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
 
 import java.io.PrintWriter;
 
@@ -174,7 +173,8 @@
         }
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
 
-        mInputConsumerController = new InputConsumerController(mWindowManager);
+        mInputConsumerController = InputConsumerController.getPipInputConsumer();
+        mInputConsumerController.registerInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager);
         mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
                 mInputConsumerController);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 9fb201b..26fced3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -23,7 +23,6 @@
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
 import android.app.RemoteAction;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
@@ -43,6 +42,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.component.HidePipMenuEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.shared.system.InputConsumerController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c0fed34..b253517 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -46,6 +46,7 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.R;
+import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9bef0ee..445fb24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -104,6 +104,7 @@
     private DeadZone mDeadZone;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
+    private boolean mRecentsAnimationStarted;
 
     // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
     final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -205,10 +206,18 @@
         }
     }
 
-    private final OverviewProxyListener mOverviewProxyListener = isConnected -> {
-        setSlippery(!isConnected);
-        setDisabledFlags(mDisabledFlags, true);
-        setUpSwipeUpOnboarding(isConnected);
+    private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+        @Override
+        public void onConnectionChanged(boolean isConnected) {
+            setSlippery(!isConnected);
+            setDisabledFlags(mDisabledFlags, true);
+            setUpSwipeUpOnboarding(isConnected);
+        }
+
+        @Override
+        public void onRecentsAnimationStarted() {
+            mRecentsAnimationStarted = true;
+        }
     };
 
     public NavigationBarView(Context context, AttributeSet attrs) {
@@ -270,12 +279,26 @@
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
-        return super.onTouchEvent(event);
+        return mRecentsAnimationStarted || super.onTouchEvent(event);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mGestureHelper.onInterceptTouchEvent(event);
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            mRecentsAnimationStarted = false;
+        } else if (action == MotionEvent.ACTION_UP) {
+            // If the overview proxy service has not started the recents animation then clean up
+            // after it to ensure that the nav bar buttons still work
+            if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
+                try {
+                    ActivityManager.getService().cancelRecentsAnimation();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not cancel recents animation");
+                }
+            }
+        }
+        return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
     }
 
     public void abortCurrentGesture() {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 7c6019e..db70184 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -196,6 +196,10 @@
     // Inform the user that unexpectedly rapid network usage is happening
     NOTE_NET_RAPID = 45;
 
+    // Notify the user that carrier Wi-Fi networks are available.
+    // Package: android
+    NOTE_CARRIER_NETWORK_AVAILABLE = 46;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 15c0f3c..342b48e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -82,6 +82,7 @@
 import java.util.TreeSet;
 import java.util.function.Predicate;
 
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
 import static android.app.AlarmManager.RTC_WAKEUP;
 import static android.app.AlarmManager.RTC;
 import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -175,7 +176,6 @@
     long mNextNonWakeupDeliveryTime;
     long mLastTimeChangeClockTime;
     long mLastTimeChangeRealtime;
-    long mAllowWhileIdleMinTime;
     int mNumTimeChanged;
 
     // Bookkeeping about the identity of the "System UI" package, determined at runtime.
@@ -199,6 +199,12 @@
      */
     final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
 
+    /**
+     * For each uid, we store whether the last allow-while-idle alarm was dispatched while
+     * the uid was in foreground or not. We will use the allow_while_idle_short_time in such cases.
+     */
+    final SparseBooleanArray mUseAllowWhileIdleShortTime = new SparseBooleanArray();
+
     final static class IdleDispatchEntry {
         int uid;
         String pkg;
@@ -242,7 +248,6 @@
         private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
                 = "allow_while_idle_whitelist_duration";
         private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
-        private static final String KEY_BG_RESTRICTIONS_ENABLED = "limit_bg_alarms_enabled";
 
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -277,7 +282,6 @@
 
         public Constants(Handler handler) {
             super(handler);
-            updateAllowWhileIdleMinTimeLocked();
             updateAllowWhileIdleWhitelistDurationLocked();
         }
 
@@ -288,11 +292,6 @@
             updateConstants();
         }
 
-        public void updateAllowWhileIdleMinTimeLocked() {
-            mAllowWhileIdleMinTime = mPendingIdleUntil != null
-                    ? ALLOW_WHILE_IDLE_LONG_TIME : ALLOW_WHILE_IDLE_SHORT_TIME;
-        }
-
         public void updateAllowWhileIdleWhitelistDurationLocked() {
             if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
                 mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -330,7 +329,6 @@
                 LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
                         DEFAULT_LISTENER_TIMEOUT);
 
-                updateAllowWhileIdleMinTimeLocked();
                 updateAllowWhileIdleWhitelistDurationLocked();
             }
         }
@@ -967,9 +965,6 @@
             }
         }
 
-        // Make sure we are using the correct ALLOW_WHILE_IDLE min time.
-        mConstants.updateAllowWhileIdleMinTimeLocked();
-
         // Reschedule everything.
         rescheduleKernelAlarmsLocked();
         updateNextAlarmClockLocked();
@@ -1421,7 +1416,6 @@
                 return;
             }
         }
-
         if (RECORD_DEVICE_IDLE_ALARMS) {
             if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
                 IdleDispatchEntry ent = new IdleDispatchEntry();
@@ -1472,7 +1466,6 @@
             }
 
             mPendingIdleUntil = a;
-            mConstants.updateAllowWhileIdleMinTimeLocked();
             needRebatch = true;
         } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
             if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
@@ -1751,6 +1744,15 @@
             if (!blocked) {
                 pw.println("    none");
             }
+            pw.print("  mUseAllowWhileIdleShortTime: [");
+            for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+                if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+                    UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
+                    pw.print(" ");
+                }
+            }
+            pw.println("]");
+
             if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
                 pw.println();
                 pw.println("    Idle mode state:");
@@ -1803,9 +1805,6 @@
                 pw.println();
             }
 
-            pw.print("  mAllowWhileIdleMinTime=");
-            TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw);
-            pw.println();
             if (mLastAllowWhileIdleDispatch.size() > 0) {
                 pw.println("  Last allow while idle dispatch times:");
                 for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
@@ -2072,8 +2071,6 @@
                 f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
             }
 
-            proto.write(AlarmManagerServiceProto.ALLOW_WHILE_IDLE_MIN_DURATION_MS,
-                    mAllowWhileIdleMinTime);
             for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
                 final long token = proto.start(
                         AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
@@ -2739,6 +2736,7 @@
     }
 
     private boolean isBackgroundRestricted(Alarm alarm) {
+        final boolean allowWhileIdle = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0;
         if (alarm.alarmClock != null) {
             // Don't block alarm clocks
             return false;
@@ -2751,7 +2749,8 @@
         final String sourcePackage =
                 (alarm.operation != null) ? alarm.operation.getCreatorPackage() : alarm.packageName;
         final int sourceUid = alarm.creatorUid;
-        return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage);
+        return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
+                allowWhileIdle);
     }
 
     private native long init();
@@ -2785,8 +2784,21 @@
                 if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
                     // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
                     // schedule such alarms.
-                    long lastTime = mLastAllowWhileIdleDispatch.get(alarm.uid, 0);
-                    long minTime = lastTime + mAllowWhileIdleMinTime;
+                    final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
+                    final boolean dozing = mPendingIdleUntil != null;
+                    final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+                    final long minTime;
+                    if (!dozing && !ebs) {
+                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+                    } else if (dozing) {
+                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+                    } else if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
+                        // if the last allow-while-idle went off while uid was fg, or the uid
+                        // recently came into fg, don't block the alarm for long.
+                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+                    } else {
+                        minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+                    }
                     if (nowELAPSED < minTime) {
                         // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
                         // alarm went off for this app.  Reschedule the alarm to be in the
@@ -3526,6 +3538,7 @@
 
         @Override public void onUidGone(int uid, boolean disabled) {
             synchronized (mLock) {
+                mUseAllowWhileIdleShortTime.delete(uid);
                 if (disabled) {
                     removeForStoppedLocked(uid);
                 }
@@ -3533,6 +3546,9 @@
         }
 
         @Override public void onUidActive(int uid) {
+            synchronized (mLock) {
+                mUseAllowWhileIdleShortTime.put(uid, true);
+            }
         }
 
         @Override public void onUidIdle(int uid, boolean disabled) {
@@ -3547,7 +3563,6 @@
         }
     };
 
-
     private final Listener mForceAppStandbyListener = new Listener() {
         @Override
         public void unblockAllUnrestrictedAlarms() {
@@ -3829,7 +3844,12 @@
 
             if (allowWhileIdle) {
                 // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
-                mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
+                mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
+                if (mForceAppStandbyTracker.isInForeground(alarm.creatorUid)) {
+                    mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
+                } else {
+                    mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
+                }
                 if (RECORD_DEVICE_IDLE_ALARMS) {
                     IdleDispatchEntry ent = new IdleDispatchEntry();
                     ent.uid = alarm.uid;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 145b307..3bfa31a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -140,6 +140,7 @@
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.MultipathPolicyTracker;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkMonitor;
@@ -511,6 +512,9 @@
     @VisibleForTesting
     final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
 
+    @VisibleForTesting
+    final MultipathPolicyTracker mMultipathPolicyTracker;
+
     /**
      * Implements support for the legacy "one network per network type" model.
      *
@@ -894,6 +898,8 @@
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
 
+        mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
+
         mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
         registerPrivateDnsSettingsCallbacks();
     }
@@ -1974,6 +1980,9 @@
         pw.println();
         dumpAvoidBadWifiSettings(pw);
 
+        pw.println();
+        mMultipathPolicyTracker.dump(pw);
+
         if (argsContain(args, SHORT_ARG) == false) {
             pw.println();
             synchronized (mValidationLogs) {
@@ -2891,6 +2900,11 @@
             return ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
         }
 
+        Integer networkPreference = mMultipathPolicyTracker.getMultipathPreference(network);
+        if (networkPreference != null) {
+            return networkPreference;
+        }
+
         return mMultinetworkPolicyTracker.getMeteredMultipathPreference();
     }
 
@@ -2984,6 +2998,7 @@
                     for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                         nai.networkMonitor.systemReady = true;
                     }
+                    mMultipathPolicyTracker.start();
                     break;
                 }
                 case EVENT_REVALIDATE_NETWORK: {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 792fdfe..257845e 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -214,8 +214,11 @@
                 int uid, @NonNull String packageName) {
             updateJobsForUidPackage(uid, packageName);
 
-            if (!sender.areAlarmsRestricted(uid, packageName)) {
+            if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
                 unblockAlarmsForUidPackage(uid, packageName);
+            } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)){
+                // we need to deliver the allow-while-idle alarms for this uid, package
+                unblockAllUnrestrictedAlarms();
             }
         }
 
@@ -706,7 +709,7 @@
                     synchronized (mLock) {
                         unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby;
                     }
-                    for (Listener l: cloneListeners()) {
+                    for (Listener l : cloneListeners()) {
                         l.updateAllJobs();
                         if (unblockAlarms) {
                             l.unblockAllUnrestrictedAlarms();
@@ -818,9 +821,10 @@
     /**
      * @return whether alarms should be restricted for a UID package-name.
      */
-    public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
+    public boolean areAlarmsRestricted(int uid, @NonNull String packageName,
+            boolean allowWhileIdle) {
         return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false,
-                /* exemptOnBatterySaver =*/ false);
+                /* exemptOnBatterySaver =*/ allowWhileIdle);
     }
 
     /**
@@ -879,7 +883,6 @@
     /**
      * @return whether force all apps standby is enabled or not.
      *
-     * Note clients normally shouldn't need to access it.
      */
     boolean isForceAllAppsStandbyEnabled() {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/MultipathPolicyTracker.java b/services/core/java/com/android/server/MultipathPolicyTracker.java
new file mode 100644
index 0000000..9e0a230
--- /dev/null
+++ b/services/core/java/com/android/server/MultipathPolicyTracker.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStatsManager.UsageCallback;
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.INetworkPolicyManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkRequest;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.net.StringNetworkSpecifier;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import java.util.Calendar;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.net.NetworkPolicyManagerInternal;
+
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
+
+/**
+ * Manages multipath data budgets.
+ *
+ * Informs the return value of ConnectivityManager#getMultipathPreference() based on:
+ * - The user's data plan, as returned by getSubscriptionOpportunisticQuota().
+ * - The amount of data usage that occurs on mobile networks while they are not the system default
+ *   network (i.e., when the app explicitly selected such networks).
+ *
+ * Currently, quota is determined on a daily basis, from midnight to midnight local time.
+ *
+ * @hide
+ */
+public class MultipathPolicyTracker {
+    private static String TAG = MultipathPolicyTracker.class.getSimpleName();
+
+    private static final boolean DBG = false;
+
+    private final Context mContext;
+    private final Handler mHandler;
+
+    private ConnectivityManager mCM;
+    private NetworkStatsManager mStatsManager;
+    private NetworkPolicyManager mNPM;
+    private TelephonyManager mTelephonyManager;
+    private INetworkStatsService mStatsService;
+
+    private NetworkCallback mMobileNetworkCallback;
+    private NetworkPolicyManager.Listener mPolicyListener;
+
+    // STOPSHIP: replace this with a configurable mechanism.
+    private static final long DEFAULT_DAILY_MULTIPATH_QUOTA = 2_500_000;
+
+    private volatile int mMeteredMultipathPreference;
+
+    public MultipathPolicyTracker(Context ctx, Handler handler) {
+        mContext = ctx;
+        mHandler = handler;
+        // Because we are initialized by the ConnectivityService constructor, we can't touch any
+        // connectivity APIs. Service initialization is done in start().
+    }
+
+    public void start() {
+        mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mNPM = (NetworkPolicyManager) mContext.getSystemService(Context.NETWORK_POLICY_SERVICE);
+        mStatsManager = (NetworkStatsManager) mContext.getSystemService(
+                Context.NETWORK_STATS_SERVICE);
+        mStatsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+
+        registerTrackMobileCallback();
+        registerNetworkPolicyListener();
+    }
+
+    public void shutdown() {
+        maybeUnregisterTrackMobileCallback();
+        unregisterNetworkPolicyListener();
+        for (MultipathTracker t : mMultipathTrackers.values()) {
+            t.shutdown();
+        }
+        mMultipathTrackers.clear();
+    }
+
+    // Called on an arbitrary binder thread.
+    public Integer getMultipathPreference(Network network) {
+        MultipathTracker t = mMultipathTrackers.get(network);
+        if (t != null) {
+            return t.getMultipathPreference();
+        }
+        return null;
+    }
+
+    // Track information on mobile networks as they come and go.
+    class MultipathTracker {
+        final Network network;
+        final int subId;
+        final String subscriberId;
+
+        private long mQuota;
+        /** Current multipath budget. Nonzero iff we have budget and a UsageCallback is armed. */
+        private long mMultipathBudget;
+        private final NetworkTemplate mNetworkTemplate;
+        private final UsageCallback mUsageCallback;
+
+        public MultipathTracker(Network network, NetworkCapabilities nc) {
+            this.network = network;
+            try {
+                subId = Integer.parseInt(
+                        ((StringNetworkSpecifier) nc.getNetworkSpecifier()).toString());
+            } catch (ClassCastException | NullPointerException | NumberFormatException e) {
+                throw new IllegalStateException(String.format(
+                        "Can't get subId from mobile network %s (%s): %s",
+                        network, nc, e.getMessage()));
+            }
+
+            TelephonyManager tele = (TelephonyManager) mContext.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            if (tele == null) {
+                throw new IllegalStateException(String.format("Missing TelephonyManager"));
+            }
+            tele = tele.createForSubscriptionId(subId);
+            if (tele == null) {
+                throw new IllegalStateException(String.format(
+                        "Can't get TelephonyManager for subId %d", subId));
+            }
+
+            subscriberId = tele.getSubscriberId();
+            mNetworkTemplate = new NetworkTemplate(
+                    NetworkTemplate.MATCH_MOBILE_ALL, subscriberId, new String[] { subscriberId },
+                    null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                    NetworkStats.DEFAULT_NETWORK_NO);
+            mUsageCallback = new UsageCallback() {
+                @Override
+                public void onThresholdReached(int networkType, String subscriberId) {
+                    if (DBG) Slog.d(TAG, "onThresholdReached for network " + network);
+                    mMultipathBudget = 0;
+                    updateMultipathBudget();
+                }
+            };
+
+            updateMultipathBudget();
+        }
+
+        private long getDailyNonDefaultDataUsage() {
+            Calendar start = Calendar.getInstance();
+            Calendar end = (Calendar) start.clone();
+            start.set(Calendar.HOUR_OF_DAY, 0);
+            start.set(Calendar.MINUTE, 0);
+            start.set(Calendar.SECOND, 0);
+            start.set(Calendar.MILLISECOND, 0);
+
+            long bytes;
+            try {
+                // TODO: Consider using NetworkStatsManager.getSummaryForDevice instead.
+                bytes = mStatsService.getNetworkTotalBytes(mNetworkTemplate,
+                        start.getTimeInMillis(), end.getTimeInMillis());
+                if (DBG) Slog.w(TAG, "Non-default data usage: " + bytes);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Can't fetch daily data usage: " + e);
+                bytes = -1;
+            } catch (IllegalStateException e) {
+                // Bandwidth control disabled?
+                bytes = -1;
+            }
+            return bytes;
+        }
+
+        void updateMultipathBudget() {
+            NetworkPolicyManagerInternal npms = LocalServices.getService(
+                    NetworkPolicyManagerInternal.class);
+            long quota = npms.getSubscriptionOpportunisticQuota(this.network, QUOTA_TYPE_MULTIPATH);
+            if (DBG) Slog.d(TAG, "Opportunistic quota from data plan: " + quota + " bytes");
+
+            if (quota == 0) {
+                // STOPSHIP: replace this with a configurable mechanism.
+                quota = DEFAULT_DAILY_MULTIPATH_QUOTA;
+                if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes");
+            }
+
+            if (haveMultipathBudget() && quota == mQuota) {
+                // If we already have a usage callback pending , there's no need to re-register it
+                // if the quota hasn't changed. The callback will simply fire as expected when the
+                // budget is spent. Also: if we re-register the callback when we're below the
+                // UsageCallback's minimum value of 2MB, we'll overshoot the budget.
+                if (DBG) Slog.d(TAG, "Quota still " + quota + ", not updating.");
+                return;
+            }
+            mQuota = quota;
+
+            long usage = getDailyNonDefaultDataUsage();
+            long budget = Math.max(0, quota - usage);
+            if (budget > 0) {
+                if (DBG) Slog.d(TAG, "Setting callback for " + budget +
+                        " bytes on network " + network);
+                registerUsageCallback(budget);
+            } else {
+                maybeUnregisterUsageCallback();
+            }
+        }
+
+        public int getMultipathPreference() {
+            if (haveMultipathBudget()) {
+                return MULTIPATH_PREFERENCE_HANDOVER | MULTIPATH_PREFERENCE_RELIABILITY;
+            }
+            return 0;
+        }
+
+        // For debugging only.
+        public long getQuota() {
+            return mQuota;
+        }
+
+        // For debugging only.
+        public long getMultipathBudget() {
+            return mMultipathBudget;
+        }
+
+        private boolean haveMultipathBudget() {
+            return mMultipathBudget > 0;
+        }
+
+        private void registerUsageCallback(long budget) {
+            maybeUnregisterUsageCallback();
+            mStatsManager.registerUsageCallback(mNetworkTemplate, TYPE_MOBILE, budget,
+                    mUsageCallback, mHandler);
+            mMultipathBudget = budget;
+        }
+
+        private void maybeUnregisterUsageCallback() {
+            if (haveMultipathBudget()) {
+                if (DBG) Slog.d(TAG, "Unregistering callback, budget was " + mMultipathBudget);
+                mStatsManager.unregisterUsageCallback(mUsageCallback);
+                mMultipathBudget = 0;
+            }
+        }
+
+        void shutdown() {
+            maybeUnregisterUsageCallback();
+        }
+    }
+
+    // Only ever updated on the handler thread. Accessed from other binder threads to retrieve
+    // the tracker for a specific network.
+    private final ConcurrentHashMap <Network, MultipathTracker> mMultipathTrackers =
+            new ConcurrentHashMap<>();
+
+    // TODO: this races with app code that might respond to onAvailable() by immediately calling
+    // getMultipathPreference. Fix this by adding to ConnectivityService the ability to directly
+    // invoke NetworkCallbacks on tightly-coupled classes such as this one which run on its
+    // handler thread.
+    private void registerTrackMobileCallback() {
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build();
+        mMobileNetworkCallback = new ConnectivityManager.NetworkCallback() {
+            @Override
+            public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+                MultipathTracker existing = mMultipathTrackers.get(network);
+                if (existing != null) {
+                    existing.updateMultipathBudget();
+                    return;
+                }
+
+                try {
+                    mMultipathTrackers.put(network, new MultipathTracker(network, nc));
+                } catch (IllegalStateException e) {
+                    Slog.e(TAG, "Can't track mobile network " + network + ": " + e.getMessage());
+                }
+                if (DBG) Slog.d(TAG, "Tracking mobile network " + network);
+            }
+
+            @Override
+            public void onLost(Network network) {
+                MultipathTracker existing = mMultipathTrackers.get(network);
+                if (existing != null) {
+                    existing.shutdown();
+                    mMultipathTrackers.remove(network);
+                }
+                if (DBG) Slog.d(TAG, "No longer tracking mobile network " + network);
+            }
+        };
+
+        mCM.registerNetworkCallback(request, mMobileNetworkCallback, mHandler);
+    }
+
+    private void maybeUnregisterTrackMobileCallback() {
+        if (mMobileNetworkCallback != null) {
+            mCM.unregisterNetworkCallback(mMobileNetworkCallback);
+        }
+        mMobileNetworkCallback = null;
+    }
+
+    private void registerNetworkPolicyListener() {
+        mPolicyListener = new NetworkPolicyManager.Listener() {
+            @Override
+            public void onMeteredIfacesChanged(String[] meteredIfaces) {
+                // Dispatched every time opportunistic quota is recalculated.
+                mHandler.post(() -> {
+                    for (MultipathTracker t : mMultipathTrackers.values()) {
+                        t.updateMultipathBudget();
+                    }
+                });
+            }
+        };
+        mNPM.registerListener(mPolicyListener);
+    }
+
+    private void unregisterNetworkPolicyListener() {
+        mNPM.unregisterListener(mPolicyListener);
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        // Do not use in production. Access to class data is only safe on the handler thrad.
+        pw.println("MultipathPolicyTracker:");
+        pw.increaseIndent();
+        for (MultipathTracker t : mMultipathTrackers.values()) {
+            pw.println(String.format("Network %s: quota %d, budget %d. Preference: %s",
+                    t.network, t.getQuota(), t.getMultipathBudget(),
+                    DebugUtils.flagsToString(ConnectivityManager.class, "MULTIPATH_PREFERENCE_",
+                            t.getMultipathPreference())));
+        }
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index db21ef1..aa8d56b 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -50,7 +51,9 @@
 import android.view.Display;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.DisplayWindowController;
 
+import com.android.server.wm.WindowContainerListener;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -58,7 +61,8 @@
  * Exactly one of these classes per Display in the system. Capable of holding zero or more
  * attached {@link ActivityStack}s.
  */
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends ConfigurationContainer<ActivityStack>
+        implements WindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
 
@@ -100,6 +104,8 @@
     // Used in updating the display size
     private Point mTmpDisplaySize = new Point();
 
+    private DisplayWindowController mWindowContainerController;
+
     ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
         mSupervisor = supervisor;
         mDisplayId = displayId;
@@ -108,10 +114,15 @@
             throw new IllegalStateException("Display does not exist displayId=" + displayId);
         }
         mDisplay = display;
+        mWindowContainerController = createWindowContainerController();
 
         updateBounds();
     }
 
+    protected DisplayWindowController createWindowContainerController() {
+        return new DisplayWindowController(mDisplayId, this);
+    }
+
     void updateBounds() {
         mDisplay.getSize(mTmpDisplaySize);
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
@@ -148,7 +159,10 @@
 
     private void positionChildAt(ActivityStack stack, int position) {
         mStacks.remove(stack);
-        mStacks.add(getTopInsertPosition(stack, position), stack);
+        final int insertPosition = getTopInsertPosition(stack, position);
+        mStacks.add(insertPosition, stack);
+        mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+                insertPosition);
     }
 
     private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
@@ -651,6 +665,64 @@
                 && (mSupervisor.mService.mRunningVoice == null);
     }
 
+    /**
+     * @return the stack currently above the home stack.  Can be null if there is no home stack, or
+     *         the home stack is already on top.
+     */
+    ActivityStack getStackAboveHome() {
+        if (mHomeStack == null) {
+            // Skip if there is no home stack
+            return null;
+        }
+
+        final int stackIndex = mStacks.indexOf(mHomeStack) + 1;
+        return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
+    }
+
+    /**
+     * Adjusts the home stack behind the last visible stack in the display if necessary. Generally
+     * used in conjunction with {@link #moveHomeStackBehindStack}.
+     */
+    void moveHomeStackBehindBottomMostVisibleStack() {
+        if (mHomeStack == null) {
+            // Skip if there is no home stack
+            return;
+        }
+
+        // Move the home stack to the bottom to not affect the following visibility checks
+        positionChildAtBottom(mHomeStack);
+
+        // Find the next position where the homes stack should be placed
+        final int numStacks = mStacks.size();
+        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            if (stack == mHomeStack) {
+                continue;
+            }
+            final int winMode = stack.getWindowingMode();
+            final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
+                    winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (stack.shouldBeVisible(null) && isValidWindowingMode) {
+                // Move the home stack to behind this stack
+                positionChildAt(mHomeStack, Math.max(0, stackNdx - 1));
+                break;
+            }
+        }
+    }
+
+    /**
+     * Moves the home stack behind the given {@param stack} if possible. If {@param stack} is not
+     * currently in the display, then then the home stack is moved to the back. Generally used in
+     * conjunction with {@link #moveHomeStackBehindBottomMostVisibleStack}.
+     */
+    void moveHomeStackBehindStack(ActivityStack behindStack) {
+        if (behindStack == null) {
+            return;
+        }
+
+        positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1));
+    }
+
     boolean isSleeping() {
         return mSleeping;
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d97ae4a..fcbcce4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,6 +39,7 @@
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -376,6 +377,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 import android.view.Gravity;
+import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
 import android.view.RemoteAnimationDefinition;
 import android.view.View;
@@ -449,6 +451,7 @@
 import com.android.server.utils.PriorityDump;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.RecentsAnimationController;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -5095,23 +5098,16 @@
     }
 
     @Override
-    public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle options,
-            Bundle activityOptions, int userId) {
-        if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
-            String msg = "Permission Denial: startRecentsActivity() from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " not recent tasks package";
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
-        final int recentsUid = mRecentTasks.getRecentsComponentUid();
-        final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
-        final String recentsPackage = recentsComponent.getPackageName();
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+                IRecentsAnimationRunner recentsAnimationRunner) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
+                final int recentsUid = mRecentTasks.getRecentsComponentUid();
+                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final String recentsPackage = recentsComponent.getPackageName();
+
                 // If provided, kick off the request for the assist data in the background before
                 // starting the activity
                 if (assistDataReceiver != null) {
@@ -5128,17 +5124,24 @@
                             recentsUid, recentsPackage);
                 }
 
-                final Intent intent = new Intent();
-                intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
-                intent.setComponent(recentsComponent);
-                intent.putExtras(options);
+                // Start a new recents animation
+                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+                        mActivityStartController, mWindowManager, mUserController);
+                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
+                        recentsUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
 
-                return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
-                        .setCallingUid(recentsUid)
-                        .setCallingPackage(recentsPackage)
-                        .setActivityOptions(safeOptions)
-                        .setMayWait(userId)
-                        .execute();
+    @Override
+    public void cancelRecentsAnimation() {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                mWindowManager.cancelRecentsAnimation();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9d06b0d..172228b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -994,12 +994,6 @@
             insertTaskAtTop(task, null);
             return;
         }
-
-        task = topTask();
-        if (task != null) {
-            mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
-                    true /* includingParents */);
-        }
     }
 
     /**
@@ -1024,12 +1018,6 @@
         if (task != null) {
             insertTaskAtBottom(task);
             return;
-        } else {
-            task = bottomTask();
-            if (task != null) {
-                mWindowContainerController.positionChildAtBottom(
-                        task.getWindowContainerController(), true /* includingParents */);
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bfb563f..510a3fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -297,6 +297,7 @@
     private RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
+    final Looper mLooper;
 
     /** Short cut */
     WindowManagerService mWindowManager;
@@ -581,6 +582,7 @@
 
     public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
         mService = service;
+        mLooper = looper;
         mHandler = new ActivityStackSupervisorHandler(looper);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4dc30dd..8fd754a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -302,6 +302,7 @@
         SafeActivityOptions activityOptions;
         boolean ignoreTargetSecurity;
         boolean componentSpecified;
+        boolean avoidMoveToFront;
         ActivityRecord[] outActivity;
         TaskRecord inTask;
         String reason;
@@ -356,6 +357,7 @@
             userId = 0;
             waitResult = null;
             mayWait = false;
+            avoidMoveToFront = false;
         }
 
         /**
@@ -390,6 +392,7 @@
             userId = request.userId;
             waitResult = request.waitResult;
             mayWait = request.mayWait;
+            avoidMoveToFront = request.avoidMoveToFront;
         }
     }
 
@@ -1485,19 +1488,23 @@
             mDoResume = false;
         }
 
-        if (mOptions != null && mOptions.getLaunchTaskId() != -1
-                && mOptions.getTaskOverlay()) {
-            r.mTaskOverlay = true;
-            if (!mOptions.canTaskOverlayResume()) {
-                final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
-                final ActivityRecord top = task != null ? task.getTopActivity() : null;
-                if (top != null && top.state != RESUMED) {
+        if (mOptions != null) {
+            if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
+                r.mTaskOverlay = true;
+                if (!mOptions.canTaskOverlayResume()) {
+                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+                            mOptions.getLaunchTaskId());
+                    final ActivityRecord top = task != null ? task.getTopActivity() : null;
+                    if (top != null && top.state != RESUMED) {
 
-                    // The caller specifies that we'd like to be avoided to be moved to the front,
-                    // so be it!
-                    mDoResume = false;
-                    mAvoidMoveToFront = true;
+                        // The caller specifies that we'd like to be avoided to be moved to the
+                        // front, so be it!
+                        mDoResume = false;
+                        mAvoidMoveToFront = true;
+                    }
                 }
+            } else if (mOptions.getAvoidMoveToFront()) {
+                mAvoidMoveToFront = true;
             }
         }
 
@@ -1838,7 +1845,7 @@
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
         mTargetStack = intentActivity.getStack();
-        if (!mMovedToFront && mDoResume) {
+        if (!mAvoidMoveToFront && !mMovedToFront && mDoResume) {
             if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
                     + " from " + intentActivity);
             mTargetStack.moveToFront("intentActivityFound");
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
new file mode 100644
index 0000000..fe576fda
--- /dev/null
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.view.IRecentsAnimationRunner;
+import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
+import com.android.server.wm.WindowManagerService;
+
+/**
+ * Manages the recents animation, including the reordering of the stacks for the transition and
+ * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
+ */
+class RecentsAnimation implements RecentsAnimationCallbacks {
+    private static final String TAG = RecentsAnimation.class.getSimpleName();
+
+    private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final ActivityStartController mActivityStartController;
+    private final WindowManagerService mWindowManager;
+    private final UserController mUserController;
+    private final Handler mHandler;
+
+    private final Runnable mCancelAnimationRunnable;
+
+    // The stack to restore the home stack behind when the animation is finished
+    private ActivityStack mRestoreHomeBehindStack;
+
+    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
+            ActivityStartController activityStartController, WindowManagerService wm,
+            UserController userController) {
+        mService = am;
+        mStackSupervisor = stackSupervisor;
+        mActivityStartController = activityStartController;
+        mHandler = new Handler(mStackSupervisor.mLooper);
+        mWindowManager = wm;
+        mUserController = userController;
+        mCancelAnimationRunnable = () -> {
+            // The caller has not finished the animation in a predefined amount of time, so
+            // force-cancel the animation
+            mWindowManager.cancelRecentsAnimation();
+        };
+    }
+
+    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
+            ComponentName recentsComponent, int recentsUid) {
+
+        // Cancel the previous recents animation if necessary
+        mWindowManager.cancelRecentsAnimation();
+
+        final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
+        if (!hasExistingHomeActivity) {
+            // No home activity
+            final ActivityOptions opts = ActivityOptions.makeBasic();
+            opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+            opts.setAvoidMoveToFront();
+            intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
+
+            mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity")
+                    .setCallingUid(recentsUid)
+                    .setCallingPackage(recentsComponent.getPackageName())
+                    .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
+                    .setMayWait(mUserController.getCurrentUserId())
+                    .execute();
+            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+
+            // TODO: Maybe wait for app to draw in this particular case?
+        }
+
+        final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+        final ActivityDisplay display = homeActivity.getDisplay();
+
+        // Save the initial position of the home activity stack to be restored to after the
+        // animation completes
+        mRestoreHomeBehindStack = hasExistingHomeActivity
+                ? display.getStackAboveHome()
+                : null;
+
+        // Move the home activity into place for the animation
+        display.moveHomeStackBehindBottomMostVisibleStack();
+
+        // Mark the home activity as launch-behind to bump its visibility for the
+        // duration of the gesture that is driven by the recents component
+        homeActivity.mLaunchTaskBehind = true;
+
+        // Fetch all the surface controls and pass them to the client to get the animation
+        // started
+        mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);
+
+        // If we updated the launch-behind state, update the visibility of the activities after we
+        // fetch the visible tasks to be controlled by the animation
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+
+        // Post a timeout for the animation
+        mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
+    }
+
+    @Override
+    public void onAnimationFinished(boolean moveHomeToTop) {
+        mHandler.removeCallbacks(mCancelAnimationRunnable);
+        synchronized (mService) {
+            if (mWindowManager.getRecentsAnimationController() == null) return;
+
+            mWindowManager.inSurfaceTransaction(() -> {
+                mWindowManager.cleanupRecentsAnimation();
+
+                // Move the home stack to the front
+                final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+                if (homeActivity == null) {
+                    return;
+                }
+
+                // Restore the launched-behind state
+                homeActivity.mLaunchTaskBehind = false;
+
+                if (moveHomeToTop) {
+                    // Bring the home stack to the front
+                    final ActivityStack homeStack = homeActivity.getStack();
+                    homeStack.mNoAnimActivities.add(homeActivity);
+                    homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+                } else {
+                    // Restore the home stack to its previous position
+                    final ActivityDisplay display = homeActivity.getDisplay();
+                    display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
+                }
+
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            });
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index ff05723..be6c4a1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -506,6 +506,7 @@
         Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
         intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final long ident = Binder.clearCallingIdentity();
         try {
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f82dc24..baea964 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -18,16 +18,19 @@
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.StatsDimensionsValue;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
@@ -80,9 +83,12 @@
 
     static final String TAG = "StatsCompanionService";
     static final boolean DEBUG = true;
+
     public static final String ACTION_TRIGGER_COLLECTION =
         "com.android.server.stats.action.TRIGGER_COLLECTION";
 
+    public static final int CODE_SUBSCRIBER_BROADCAST = 1;
+
     private final Context mContext;
     private final AlarmManager mAlarmManager;
     @GuardedBy("sStatsdLock")
@@ -151,10 +157,37 @@
 
     @Override
     public void sendBroadcast(String pkg, String cls) {
+        // TODO: Use a pending intent, and enfoceCallingPermission.
         mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
                 UserHandle.SYSTEM);
     }
 
+    @Override
+    public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
+                                        long subscriptionId, long subscriptionRuleId,
+                                        StatsDimensionsValue dimensionsValue) {
+        if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
+        enforceCallingPermission();
+        IntentSender intentSender = new IntentSender(intentSenderBinder);
+        Intent intent = new Intent()
+                .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+                .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+                .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+        try {
+            intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
+                    + "; presumably it had been cancelled.");
+            if (DEBUG) {
+                Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
+                        configUid, configKey, subscriptionId,
+                        subscriptionRuleId, dimensionsValue));
+            }
+        }
+    }
+
     private final static int[] toIntArray(List<Integer> list) {
         int[] ret = new int[list.size()];
         for (int i = 0; i < ret.length; i++) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6dc384a..3f49f0c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1508,6 +1508,10 @@
         return mTaskStackContainers.getTopStack();
     }
 
+    ArrayList<Task> getVisibleTasks() {
+        return mTaskStackContainers.getVisibleTasks();
+    }
+
     void onStackWindowingModeChanged(TaskStack stack) {
         mTaskStackContainers.onStackWindowingModeChanged(stack);
     }
@@ -1802,6 +1806,11 @@
         getParent().positionChildAt(position, this, includingParents);
     }
 
+    void positionStackAt(int position, TaskStack child) {
+        mTaskStackContainers.positionChildAt(position, child, false /* includingParents */);
+        layoutAndAssignWindowLayersIfNeeded();
+    }
+
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -3255,6 +3264,16 @@
             return mSplitScreenPrimaryStack;
         }
 
+        ArrayList<Task> getVisibleTasks() {
+            final ArrayList<Task> visibleTasks = new ArrayList<>();
+            forAllTasks(task -> {
+                if (task.isVisible()) {
+                    visibleTasks.add(task);
+                }
+            });
+            return visibleTasks;
+        }
+
         /**
          * Adds the stack to this container.
          * @see DisplayContent#createStack(int, boolean, StackWindowController)
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
new file mode 100644
index 0000000..ad4957e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.res.Configuration;
+import android.util.Slog;
+
+/**
+ * Controller for the display container. This is created by activity manager to link activity
+ * displays to the display content they use in window manager.
+ */
+public class DisplayWindowController
+        extends WindowContainerController<DisplayContent, WindowContainerListener> {
+
+    private final int mDisplayId;
+
+    public DisplayWindowController(int displayId, WindowContainerListener listener) {
+        super(listener, WindowManagerService.getInstance());
+        mDisplayId = displayId;
+
+        synchronized (mWindowMap) {
+            // TODO: Convert to setContainer() from DisplayContent once everything is hooked up.
+            // Currently we are not setup to register for config changes.
+            mContainer = mRoot.getDisplayContentOrCreate(displayId);
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Trying to add displayId=" + displayId);
+            }
+        }
+    }
+
+    @Override
+    public void removeContainer() {
+        // TODO: Pipe through from ActivityDisplay to remove the display
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+    @Override
+    public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        // TODO: Pipe through from ActivityDisplay to update the configuration for the display
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+    /**
+     * Positions the task stack at the given position in the task stack container.
+     */
+    public void positionChildAt(StackWindowController child, int position) {
+        synchronized (mWindowMap) {
+            if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
+                    + " at " + position);
+            if (mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionTaskStackAt: could not find display=" + mContainer);
+                return;
+            }
+            if (child.mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionTaskStackAt: could not find stack=" + this);
+                return;
+            }
+            mContainer.positionStackAt(position, child.mContainer);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "{DisplayWindowController displayId=" + mDisplayId + "}";
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88b7a11..281e0a844 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -86,6 +87,7 @@
     private boolean mAddInputConsumerHandle;
     private boolean mAddPipInputConsumerHandle;
     private boolean mAddWallpaperInputConsumerHandle;
+    private boolean mAddRecentsAnimationInputConsumerHandle;
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
@@ -612,7 +614,7 @@
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
         InputConsumerImpl wallpaperInputConsumer;
-        Rect pipTouchableBounds;
+        InputConsumerImpl recentsAnimationInputConsumer;
         boolean inDrag;
         WallpaperController wallpaperController;
 
@@ -622,11 +624,13 @@
             navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY);
             pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY);
             wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER, DEFAULT_DISPLAY);
+            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION,
+                    DEFAULT_DISPLAY);
             mAddInputConsumerHandle = navInputConsumer != null;
             mAddPipInputConsumerHandle = pipInputConsumer != null;
             mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
+            mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
             mTmpRect.setEmpty();
-            pipTouchableBounds = mAddPipInputConsumerHandle ? mTmpRect : null;
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
             wallpaperController = mService.mRoot.mWallpaperController;
@@ -659,12 +663,28 @@
             final boolean hasFocus = w == mInputFocus;
             final boolean isVisible = w.isVisibleLw();
 
+            if (mAddRecentsAnimationInputConsumerHandle) {
+                final RecentsAnimationController recentsAnimationController =
+                        mService.getRecentsAnimationController();
+                if (recentsAnimationController != null
+                        && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+                    if (recentsAnimationController.updateInputConsumerForApp(
+                            recentsAnimationInputConsumer, hasFocus)) {
+                        addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+                        mAddRecentsAnimationInputConsumerHandle = false;
+                    }
+                    // Skip adding the window below regardless of whether there is an input consumer
+                    // to handle it
+                    return;
+                }
+            }
+
             if (w.inPinnedWindowingMode()) {
                 if (mAddPipInputConsumerHandle
                         && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
-                    w.getBounds(pipTouchableBounds);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
+                    w.getBounds(mTmpRect);
+                    pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
                     addInputWindowHandle(pipInputConsumer.mWindowHandle);
                     mAddPipInputConsumerHandle = false;
                 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
new file mode 100644
index 0000000..c7d4b8e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.WindowConfiguration;
+import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls a single instance of the remote driven recents animation. In particular, this allows
+ * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
+ * runner is provided an animation controller which allows it to take screenshots and to notify
+ * window manager when the animation is completed. In addition, window manager may also notify the
+ * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
+ */
+public class RecentsAnimationController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
+    private static final boolean DEBUG = false;
+
+    private final WindowManagerService mService;
+    private final IRecentsAnimationRunner mRunner;
+    private final RecentsAnimationCallbacks mCallbacks;
+    private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+
+    // The recents component app token that is shown behind the visibile tasks
+    private AppWindowToken mHomeAppToken;
+
+    // We start the RecentsAnimationController in a pending-start state since we need to wait for
+    // the wallpaper/activity to draw before we can give control to the handler to start animating
+    // the visible task surfaces
+    private boolean mPendingStart = true;
+
+    // Set when the animation has been canceled
+    private boolean mCanceled = false;
+
+    // Whether or not the input consumer is enabled. The input consumer must be both registered and
+    // enabled for it to start intercepting touch events.
+    private boolean mInputConsumerEnabled;
+
+    private Rect mTmpRect = new Rect();
+
+    public interface RecentsAnimationCallbacks {
+        void onAnimationFinished(boolean moveHomeToTop);
+    }
+
+    private final IRecentsAnimationController mController =
+            new IRecentsAnimationController.Stub() {
+
+        @Override
+        public TaskSnapshot screenshotTask(int taskId) {
+            if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return null;
+                    }
+                    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+                        final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+                        final Task task = adapter.mTask;
+                        if (task.mTaskId == taskId) {
+                            // TODO: Save this screenshot as the task snapshot?
+                            final Rect taskFrame = new Rect();
+                            task.getBounds(taskFrame);
+                            final GraphicBuffer buffer = SurfaceControl.captureLayers(
+                                    task.getSurfaceControl().getHandle(), taskFrame, 1f);
+                            final AppWindowToken topChild = task.getTopChild();
+                            final WindowState mainWindow = topChild.findMainWindow();
+                            return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
+                                    mainWindow.mStableInsets,
+                                    ActivityManager.isLowRamDeviceStatic() /* reduced */,
+                                    1.0f /* scale */);
+                        }
+                    }
+                    return null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void finish(boolean moveHomeToTop) {
+            if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return;
+                    }
+                }
+
+                // Note, the callback will handle its own synchronization, do not lock on WM lock
+                // prior to calling the callback
+                mCallbacks.onAnimationFinished(moveHomeToTop);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setInputConsumerEnabled(boolean enabled) {
+            if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
+                    + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return;
+                    }
+
+                    mInputConsumerEnabled = enabled;
+                    mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+                    mService.scheduleAnimationLocked();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    /**
+     * Initializes a new RecentsAnimationController.
+     *
+     * @param remoteAnimationRunner The remote runner which should be notified when the animation is
+     *                              ready to start or has been canceled
+     * @param callbacks Callbacks to be made when the animation finishes
+     * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
+     *                                 animation is complete. Will be passed to the callback.
+     */
+    RecentsAnimationController(WindowManagerService service,
+            IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
+            int displayId) {
+        mService = service;
+        mRunner = remoteAnimationRunner;
+        mCallbacks = callbacks;
+
+        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
+        if (visibleTasks.isEmpty()) {
+            cancelAnimation();
+            return;
+        }
+
+        // Make leashes for each of the visible tasks and add it to the recents animation to be
+        // started
+        final int taskCount = visibleTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            final Task task = visibleTasks.get(i);
+            final WindowConfiguration config = task.getWindowConfiguration();
+            if (config.tasksAreFloating()
+                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    || config.getActivityType() == ACTIVITY_TYPE_HOME) {
+                continue;
+            }
+            addAnimation(task);
+        }
+
+        // Adjust the wallpaper visibility for the showing home activity
+        final AppWindowToken recentsComponentAppToken =
+                dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
+        if (recentsComponentAppToken != null) {
+            if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
+            mHomeAppToken = recentsComponentAppToken;
+            final WallpaperController wc = dc.mWallpaperController;
+            if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
+                dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                dc.setLayoutNeeded();
+            }
+        }
+
+        mService.mWindowPlacerLocked.performSurfacePlacement();
+    }
+
+    private void addAnimation(Task task) {
+        if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+        final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
+                mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
+        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
+        anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
+        task.commitPendingTransaction();
+        mPendingAnimations.add(taskAdapter);
+    }
+
+    void startAnimation() {
+        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
+        if (!mPendingStart) {
+            return;
+        }
+        try {
+            final RemoteAnimationTarget[] appAnimations =
+                    new RemoteAnimationTarget[mPendingAnimations.size()];
+            for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+                appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
+            }
+            mPendingStart = false;
+            mRunner.onAnimationStart(mController, appAnimations);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to start recents animation", e);
+        }
+    }
+
+    void cancelAnimation() {
+        if (DEBUG) Log.d(TAG, "cancelAnimation()");
+        if (mCanceled) {
+            // We've already canceled the animation
+            return;
+        }
+        mCanceled = true;
+        try {
+            mRunner.onAnimationCanceled();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to cancel recents animation", e);
+        }
+
+        // Clean up and return to the previous app
+        mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
+    }
+
+    void cleanupAnimation() {
+        if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
+                + mPendingAnimations.size());
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+            adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+        }
+        mPendingAnimations.clear();
+
+        mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+        mService.scheduleAnimationLocked();
+        mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+    }
+
+    void checkAnimationReady(WallpaperController wallpaperController) {
+        if (mPendingStart) {
+            final boolean wallpaperReady = !isHomeAppOverWallpaper()
+                    || (wallpaperController.getWallpaperTarget() != null
+                            && wallpaperController.wallpaperTransitionReady());
+            if (wallpaperReady) {
+                mService.getRecentsAnimationController().startAnimation();
+            }
+        }
+    }
+
+    boolean isWallpaperVisible(WindowState w) {
+        return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken
+                && isHomeAppOverWallpaper();
+    }
+
+    boolean hasInputConsumerForApp(AppWindowToken appToken) {
+        return mInputConsumerEnabled && isAnimatingApp(appToken);
+    }
+
+    boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer,
+            boolean hasFocus) {
+        // Update the input consumer touchable region to match the home app main window
+        final WindowState homeAppMainWindow = mHomeAppToken != null
+                ? mHomeAppToken.findMainWindow()
+                : null;
+        if (homeAppMainWindow != null) {
+            homeAppMainWindow.getBounds(mTmpRect);
+            recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus;
+            recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isHomeAppOverWallpaper() {
+        if (mHomeAppToken == null) {
+            return false;
+        }
+        return mHomeAppToken.windowsCanBeWallpaperTarget();
+    }
+
+    private boolean isAnimatingApp(AppWindowToken appToken) {
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            final Task task = mPendingAnimations.get(i).mTask;
+            for (int j = task.getChildCount() - 1; j >= 0; j--) {
+                final AppWindowToken app = task.getChildAt(j);
+                if (app == appToken) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private class TaskAnimationAdapter implements AnimationAdapter {
+
+        private Task mTask;
+        private SurfaceControl mCapturedLeash;
+        private OnAnimationFinishedCallback mCapturedFinishCallback;
+
+        TaskAnimationAdapter(Task task) {
+            mTask = task;
+        }
+
+        RemoteAnimationTarget createRemoteAnimationApp() {
+            // TODO: Do we need position and stack bounds?
+            return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+                    !mTask.fillsParent(),
+                    mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
+                    mTask.getPrefixOrderIndex(), new Point(), new Rect(),
+                    mTask.getWindowConfiguration());
+        }
+
+        @Override
+        public boolean getDetachWallpaper() {
+            return false;
+        }
+
+        @Override
+        public int getBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void startAnimation(SurfaceControl animationLeash, Transaction t,
+                OnAnimationFinishedCallback finishCallback) {
+            mCapturedLeash = animationLeash;
+            mCapturedFinishCallback = finishCallback;
+        }
+
+        @Override
+        public void onAnimationCancelled(SurfaceControl animationLeash) {
+            cancelAnimation();
+        }
+
+        @Override
+        public long getDurationHint() {
+            return 0;
+        }
+
+        @Override
+        public long getStatusBarTransitionsStartTime() {
+            return SystemClock.uptimeMillis();
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
+        pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+        pw.print(innerPrefix); pw.println("mHomeAppToken=" + mHomeAppToken);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8515dcb..7d4eafb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -160,7 +160,8 @@
             return new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
                     mainWindow.mWinAnimator.mLastClipRect,
-                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
+                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
+                    task.getWindowConfiguration());
         }
 
         private int getMode() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2cc96c9..deed7f1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -623,6 +623,13 @@
                         defaultDisplay.pendingLayoutChanges);
         }
 
+        // Defer starting the recents animation until the wallpaper has drawn
+        final RecentsAnimationController recentsAnimationController =
+            mService.getRecentsAnimationController();
+        if (recentsAnimationController != null) {
+            recentsAnimationController.checkAnimationReady(mWallpaperController);
+        }
+
         if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
                 && !mService.mAppTransition.isReady()) {
             // At this point, there was a window with a wallpaper that was force hiding other
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 10f1c3a..0512a08 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -62,7 +62,7 @@
      * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
      *                                surfaces in WM. Can be implemented differently during testing.
      */
-    SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
+    SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
             Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
         mAnimatable = animatable;
         mService = service;
@@ -71,7 +71,8 @@
                 addAfterPrepareSurfaces);
     }
 
-    private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback,
+    private OnAnimationFinishedCallback getFinishedCallback(
+            @Nullable Runnable animationFinishedCallback,
             Consumer<Runnable> addAfterPrepareSurfaces) {
         return anim -> {
             synchronized (mService.mWindowMap) {
@@ -97,7 +98,9 @@
                     SurfaceControl.openTransaction();
                     try {
                         reset(t, true /* destroyLeash */);
-                        animationFinishedCallback.run();
+                        if (animationFinishedCallback != null) {
+                            animationFinishedCallback.run();
+                        }
                     } finally {
                         SurfaceControl.mergeToGlobalTransaction(t);
                         SurfaceControl.closeTransaction();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1218d3b..f2ad6fb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -149,8 +149,17 @@
             mFindResults.setUseTopWallpaperAsTarget(true);
         }
 
+        final RecentsAnimationController recentsAnimationController =
+                mService.getRecentsAnimationController();
         final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
-        if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+        final boolean isRecentsTransitionTarget = (recentsAnimationController != null
+                && recentsAnimationController.isWallpaperVisible(w));
+        if (isRecentsTransitionTarget) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
+            mFindResults.setWallpaperTarget(w);
+            return true;
+        } else if (hasWallpaper && w.isOnScreen()
+                && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
             mFindResults.setWallpaperTarget(w);
             if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
@@ -199,15 +208,22 @@
         }
     }
 
-    private boolean isWallpaperVisible(WindowState wallpaperTarget) {
+    private final boolean isWallpaperVisible(WindowState wallpaperTarget) {
+        final RecentsAnimationController recentsAnimationController =
+                mService.getRecentsAnimationController();
+        boolean isAnimatingWithRecentsComponent = recentsAnimationController != null
+                && recentsAnimationController.isWallpaperVisible(wallpaperTarget);
         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                 + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
                 ? wallpaperTarget.mAppToken.isSelfAnimating() : null)
-                + " prev=" + mPrevWallpaperTarget);
+                + " prev=" + mPrevWallpaperTarget
+                + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
         return (wallpaperTarget != null
-                && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
-                && wallpaperTarget.mAppToken.isSelfAnimating())))
+                && (!wallpaperTarget.mObscured
+                        || isAnimatingWithRecentsComponent
+                        || (wallpaperTarget.mAppToken != null
+                                && wallpaperTarget.mAppToken.isSelfAnimating())))
                 || mPrevWallpaperTarget != null;
     }
 
@@ -587,6 +603,11 @@
             mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
             if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
                     "*** WALLPAPER DRAW TIMEOUT");
+
+            // If there was a recents animation in progress, cancel that animation
+            if (mService.getRecentsAnimationController() != null) {
+                mService.getRecentsAnimationController().cancelAnimation();
+            }
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 42c6ec2..a0b59ef 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -337,9 +337,9 @@
     }
 
     /** Returns true if this window container has the input child. */
-    boolean hasChild(WindowContainer child) {
+    boolean hasChild(E child) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer current = mChildren.get(i);
+            final E current = mChildren.get(i);
             if (current == child || current.hasChild(child)) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index de1e7ec..4fb2390 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,8 @@
 import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -123,6 +125,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IAssistDataReceiver;
+import android.app.WindowConfiguration;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -196,6 +199,7 @@
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
 import android.view.IPinnedStackListener;
+import android.view.IRecentsAnimationRunner;
 import android.view.IRotationWatcher;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindow;
@@ -528,6 +532,7 @@
     IInputMethodManager mInputMethodManager;
 
     AccessibilityController mAccessibilityController;
+    private RecentsAnimationController mRecentsAnimationController;
 
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
@@ -2670,6 +2675,39 @@
         }
     }
 
+    public void initializeRecentsAnimation(
+            IRecentsAnimationRunner recentsAnimationRunner,
+            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) {
+        synchronized (mWindowMap) {
+            cancelRecentsAnimation();
+            mRecentsAnimationController = new RecentsAnimationController(this,
+                    recentsAnimationRunner, callbacks, displayId);
+        }
+    }
+
+    public RecentsAnimationController getRecentsAnimationController() {
+        return mRecentsAnimationController;
+    }
+
+    public void cancelRecentsAnimation() {
+        synchronized (mWindowMap) {
+            if (mRecentsAnimationController != null) {
+                // This call will call through to cleanupAnimation() below after the animation is
+                // canceled
+                mRecentsAnimationController.cancelAnimation();
+            }
+        }
+    }
+
+    public void cleanupRecentsAnimation() {
+        synchronized (mWindowMap) {
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.cleanupAnimation();
+                mRecentsAnimationController = null;
+            }
+        }
+    }
+
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
         synchronized (mWindowMap) {
             final AppWindowToken atoken = mRoot.getAppWindowToken(token);
@@ -6327,6 +6365,10 @@
             pw.print("  mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
             pw.println("  mLayoutToAnim:");
             mAppTransition.dump(pw, "    ");
+            if (mRecentsAnimationController != null) {
+                pw.print("  mRecentsAnimationController="); pw.println(mRecentsAnimationController);
+                mRecentsAnimationController.dump(pw, "    ");
+            }
         }
     }
 
diff --git a/services/net/java/android/net/util/MultinetworkPolicyTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
index 424e40d..30c5cd9 100644
--- a/services/net/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
@@ -122,6 +122,7 @@
         return mAvoidBadWifi;
     }
 
+    // TODO: move this to MultipathPolicyTracker.
     public int getMeteredMultipathPreference() {
         return mMeteredMultipathPreference;
     }
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index a29e169..b68bf2d 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -266,7 +266,7 @@
         assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
                 instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver));
         assertEquals(((restrictionTypes & ALARMS_ONLY) != 0),
-                instance.areAlarmsRestricted(uid, packageName));
+                instance.areAlarmsRestricted(uid, packageName, exemptFromBatterySaver));
     }
 
     private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 96bf49b..10253c5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 
+import com.android.server.wm.DisplayWindowController;
 import org.mockito.invocation.InvocationOnMock;
 
 import android.app.IApplicationThread;
@@ -345,7 +346,7 @@
         }
     }
 
-    private static class TestActivityDisplay extends ActivityDisplay {
+    protected static class TestActivityDisplay extends ActivityDisplay {
 
         private final ActivityStackSupervisor mSupervisor;
         TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
@@ -374,6 +375,11 @@
                         this, stackId, mSupervisor, windowingMode, activityType, onTop);
             }
         }
+
+        @Override
+        protected DisplayWindowController createWindowContainerController() {
+            return mock(DisplayWindowController.class);
+        }
     }
 
     private static WindowManagerService prepareMockWindowManager() {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5a21102..24566fc 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -573,7 +573,8 @@
         assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
         assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
         assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
-                null, 0));
+                null));
+        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation());
     }
 
     private void testGetTasksApis(boolean expectCallable) {
@@ -676,8 +677,8 @@
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
-            mOtherDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+            mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+            mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
             attachDisplay(mOtherDisplay);
             attachDisplay(mDisplay);
         }
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index fc75628..c6ce7e1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -68,7 +68,7 @@
         // Create a number of stacks with tasks (of incrementing active time)
         final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
         final SparseArray<ActivityDisplay> displays = new SparseArray<>();
-        final ActivityDisplay display = new ActivityDisplay(supervisor, DEFAULT_DISPLAY);
+        final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
         displays.put(DEFAULT_DISPLAY, display);
 
         final int numStacks = 2;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d17bdc8..6799417 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1960,6 +1960,15 @@
         }
     }
 
+    /** {@hide} */
+    final void internalOnHandoverComplete() {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(() -> callback.onHandoverComplete(call));
+        }
+    }
+
     private void fireStateChanged(final int newState) {
         for (CallbackRecord<Callback> record : mCallbackRecords) {
             final Call call = this;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 7522443..63f970a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2801,6 +2801,15 @@
     public void onCallEvent(String event, Bundle extras) {}
 
     /**
+     * Notifies this {@link Connection} that a handover has completed.
+     * <p>
+     * A handover is initiated with {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int,
+     * Bundle)} on the initiating side of the handover, and
+     * {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)}.
+     */
+    public void onHandoverComplete() {}
+
+    /**
      * Notifies this {@link Connection} of a change to the extras made outside the
      * {@link ConnectionService}.
      * <p>
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 6af01ae..c1040ad 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -140,6 +140,7 @@
     private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
     private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
     private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+    private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
     private static final String SESSION_START_RTT = "CS.+RTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
@@ -179,6 +180,7 @@
     private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
     private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
     private static final int MSG_HANDOVER_FAILED = 32;
+    private static final int MSG_HANDOVER_COMPLETE = 33;
 
     private static Connection sNullConnection;
 
@@ -298,6 +300,19 @@
         }
 
         @Override
+        public void handoverComplete(String callId, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void abort(String callId, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_ABORT);
             try {
@@ -1028,6 +1043,19 @@
                     }
                     break;
                 }
+                case MSG_HANDOVER_COMPLETE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg2,
+                                SESSION_HANDLER + SESSION_HANDOVER_COMPLETE);
+                        String callId = (String) args.arg1;
+                        notifyHandoverComplete(callId);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_ON_EXTRAS_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -1445,19 +1473,24 @@
             final ConnectionRequest request,
             boolean isIncoming,
             boolean isUnknown) {
+        boolean isLegacyHandover = request.getExtras() != null &&
+                request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
+        boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
+                TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
         Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
-                        "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
-                isIncoming,
-                isUnknown);
+                        "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
+                callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
+                isHandover);
 
         Connection connection = null;
-        if (getApplicationContext().getApplicationInfo().targetSdkVersion >
-                Build.VERSION_CODES.O_MR1 && request.getExtras() != null &&
-                request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) {
+        if (isHandover) {
+            PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
+                    ? (PhoneAccountHandle) request.getExtras().getParcelable(
+                    TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
             if (!isIncoming) {
-                connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
+                connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
             } else {
-                connection = onCreateIncomingHandoverConnection(callManagerAccount, request);
+                connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
             }
         } else {
             connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -1754,6 +1787,19 @@
     }
 
     /**
+     * Notifies a {@link Connection} that a handover has completed.
+     *
+     * @param callId The ID of the call which completed handover.
+     */
+    private void notifyHandoverComplete(String callId) {
+        Log.d(this, "notifyHandoverComplete(%s)", callId);
+        Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
+        if (connection != null) {
+            connection.onHandoverComplete();
+        }
+    }
+
+    /**
      * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
      * <p>
      * These extra changes can originate from Telecom itself, or from an {@link InCallService} via
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 74fa62d..fcf04c9 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -81,6 +81,7 @@
     private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
     private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
     private static final int MSG_ON_HANDOVER_FAILED = 12;
+    private static final int MSG_ON_HANDOVER_COMPLETE = 13;
 
     /** Default Handler used to consolidate binder method calls onto a single thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -157,6 +158,11 @@
                     mPhone.internalOnHandoverFailed(callId, error);
                     break;
                 }
+                case MSG_ON_HANDOVER_COMPLETE: {
+                    String callId = (String) msg.obj;
+                    mPhone.internalOnHandoverComplete(callId);
+                    break;
+                }
                 default:
                     break;
             }
@@ -237,6 +243,11 @@
         public void onHandoverFailed(String callId, int error) {
             mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget();
         }
+
+        @Override
+        public void onHandoverComplete(String callId) {
+            mHandler.obtainMessage(MSG_ON_HANDOVER_COMPLETE, callId).sendToTarget();
+        }
     }
 
     private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index b5394b9..99f94f2 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -230,6 +230,13 @@
         }
     }
 
+    final void internalOnHandoverComplete(String callId) {
+        Call call = mCallByTelecomCallId.get(callId);
+        if (call != null) {
+            call.internalOnHandoverComplete();
+        }
+    }
+
     /**
      * Called to destroy the phone and cleanup any lingering calls.
      */
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3f5b78a..1fe5db5 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -385,6 +385,17 @@
     public static final String EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER";
 
     /**
+     * When {@code true} indicates that a request to create a new connection is for the purpose of
+     * a handover.  Note: This is used with the
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} API as part of the
+     * internal communication mechanism with the {@link android.telecom.ConnectionService}.  It is
+     * not the same as the legacy {@link #EXTRA_IS_HANDOVER} extra.
+     * @hide
+     */
+    public static final String EXTRA_IS_HANDOVER_CONNECTION =
+            "android.telecom.extra.IS_HANDOVER_CONNECTION";
+
+    /**
      * Parcelable extra used with {@link #EXTRA_IS_HANDOVER} to indicate the source
      * {@link PhoneAccountHandle} when initiating a handover which {@link ConnectionService}
      * the handover is from.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 3d04bfc..3a84f00 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -107,4 +107,6 @@
 
     void handoverFailed(String callId, in ConnectionRequest request,
             int error, in Session.Info sessionInfo);
+
+    void handoverComplete(String callId, in Session.Info sessionInfo);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 110109e..b9563fa 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -56,4 +56,6 @@
     void onRttInitiationFailure(String callId, int reason);
 
     void onHandoverFailed(String callId, int error);
+
+    void onHandoverComplete(String callId);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cbc9428..91d86c6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,13 +39,29 @@
     private final static String TAG = "CarrierConfigManager";
 
     /**
+     * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the slot index that the
+     * broadcast is for.
+     */
+    public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
+    /**
+     * Optional extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the
+     * subscription index that the broadcast is for, if a valid one is available.
+     */
+    public static final String EXTRA_SUBSCRIPTION_INDEX =
+            SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+
+    /**
      * @hide
      */
     public CarrierConfigManager() {
     }
 
     /**
-     * This intent is broadcast by the system when carrier config changes.
+     * This intent is broadcast by the system when carrier config changes. An int is specified in
+     * {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
+     * {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
+     * one is available for the slot index.
      */
     public static final String
             ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 9f8b3a8..c095438 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -837,6 +837,13 @@
     }
 
     /**
+     * Strip all the trailing 'F' characters of a string, e.g., an ICCID.
+     */
+    public static String stripTrailingFs(String s) {
+        return s == null ? null : s.replaceAll("(?i)f*$", "");
+    }
+
+    /**
      * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
      * hex number, 0 will be returned.
      */