Merge "Override hashcode, equals function for PnoNetwork" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 7f6a7c1..11f5c41 100644
--- a/Android.bp
+++ b/Android.bp
@@ -472,7 +472,7 @@
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd-stubs-module_libs_api",
         "framework-permission-stubs-systemapi",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-systemapi",
         "framework-tethering-stubs",
     ],
     installable: true,
@@ -522,7 +522,7 @@
         "framework-permission-stubs-systemapi",
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd-stubs-module_libs_api",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-systemapi",
         "framework-tethering-stubs",
         // TODO (b/147688669) should be framework-telephony-stubs
         "framework-telephony",
diff --git a/api/current.txt b/api/current.txt
index c8a7956..1227006 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5943,7 +5943,7 @@
     method public boolean isImportantConversation();
     method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
-    method public void setConversationId(@Nullable String, @Nullable String);
+    method public void setConversationId(@NonNull String, @NonNull String);
     method public void setDescription(String);
     method public void setGroup(String);
     method public void setImportance(int);
@@ -5956,7 +5956,6 @@
     method public boolean shouldShowLights();
     method public boolean shouldVibrate();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
     field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
     field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 85ad66f..f84b415 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7199,9 +7199,9 @@
   }
 
   public final class SoftApCapability implements android.os.Parcelable {
+    method public boolean areFeaturesSupported(long);
     method public int describeContents();
     method public int getMaxSupportedClients();
-    method public boolean isFeatureSupported(long);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
     field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 2229e1c..d79123b 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -20,9 +20,9 @@
 
 #include <dirent.h>
 #include <errno.h>
-
 #include <mutex>
 #include <set>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -42,6 +42,7 @@
 #include "frameworks/base/core/proto/android/os/backtrace.proto.h"
 #include "frameworks/base/core/proto/android/os/data.proto.h"
 #include "frameworks/base/core/proto/android/util/log.proto.h"
+#include "frameworks/base/core/proto/android/util/textdump.proto.h"
 #include "incidentd_util.h"
 
 namespace android {
@@ -135,7 +136,7 @@
     status_t ihStatus = wait_child(pid);
     if (ihStatus != NO_ERROR) {
         ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
-        return ihStatus;
+        return OK; // Not a fatal error.
     }
 
     return writer->writeSection(buffer);
@@ -234,7 +235,7 @@
     Fpipe pipe;
 
     // Lock protects these fields
-    mutex lock;
+    std::mutex lock;
     bool workerDone;
     status_t workerError;
 
@@ -261,83 +262,47 @@
     }
 }
 
-static void* worker_thread_func(void* cookie) {
-    // Don't crash the service if we write to a closed pipe (which can happen if
-    // dumping times out).
-    signal(SIGPIPE, sigpipe_handler);
-
-    WorkerThreadData* data = (WorkerThreadData*)cookie;
-    status_t err = data->section->BlockingCall(data->pipe.writeFd());
-
-    {
-        unique_lock<mutex> lock(data->lock);
-        data->workerDone = true;
-        data->workerError = err;
-    }
-
-    data->pipe.writeFd().reset();
-    data->decStrong(data->section);
-    // data might be gone now. don't use it after this point in this thread.
-    return NULL;
-}
-
 status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
     status_t err = NO_ERROR;
-    pthread_t thread;
-    pthread_attr_t attr;
     bool workerDone = false;
     FdBuffer buffer;
 
-    // Data shared between this thread and the worker thread.
-    sp<WorkerThreadData> data = new WorkerThreadData(this);
-
-    // Create the pipe
-    if (!data->pipe.init()) {
+    // Create shared data and pipe
+    WorkerThreadData data(this);
+    if (!data.pipe.init()) {
         return -errno;
     }
 
-    // Create the thread
-    err = pthread_attr_init(&attr);
-    if (err != 0) {
-        return -err;
-    }
-    // TODO: Do we need to tweak thread priority?
-    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    if (err != 0) {
-        pthread_attr_destroy(&attr);
-        return -err;
-    }
-
-    // The worker thread needs a reference and we can't let the count go to zero
-    // if that thread is slow to start.
-    data->incStrong(this);
-
-    err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
-    pthread_attr_destroy(&attr);
-    if (err != 0) {
-        data->decStrong(this);
-        return -err;
-    }
+    std::thread([&]() {
+        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
+        signal(SIGPIPE, sigpipe_handler);
+        status_t err = data.section->BlockingCall(data.pipe.writeFd());
+        {
+            std::unique_lock<std::mutex> lock(data.lock);
+            data.workerDone = true;
+            data.workerError = err;
+            // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
+            // the other thread executes to the end, calling ~Fpipe, which is a race condition.
+            data.pipe.writeFd().reset();
+        }
+    }).detach();
 
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
-    err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
+    err = buffer.read(data.pipe.readFd().get(), this->timeoutMs);
     if (err != NO_ERROR) {
         ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
     }
 
-    // Done with the read fd. The worker thread closes the write one so
-    // we never race and get here first.
-    data->pipe.readFd().reset();
-
     // If the worker side is finished, then return its error (which may overwrite
     // our possible error -- but it's more interesting anyway). If not, then we timed out.
     {
-        unique_lock<mutex> lock(data->lock);
-        if (data->workerError != NO_ERROR) {
-            err = data->workerError;
+        std::unique_lock<std::mutex> lock(data.lock);
+        data.pipe.close();
+        if (data.workerError != NO_ERROR) {
+            err = data.workerError;
             ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
         }
-        workerDone = data->workerDone;
+        workerDone = data.workerDone;
     }
 
     writer->setSectionStats(buffer);
@@ -473,6 +438,77 @@
 }
 
 // ================================================================================
+TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...)
+    : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) {
+    name = "dumpsys ";
+    name += service;
+
+    va_list args;
+    va_start(args, service);
+    while (true) {
+        const char* arg = va_arg(args, const char*);
+        if (arg == NULL) {
+            break;
+        }
+        mArgs.add(String16(arg));
+        name += " ";
+        name += arg;
+    }
+    va_end(args);
+}
+
+TextDumpsysSection::~TextDumpsysSection() {}
+
+status_t TextDumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
+    // checkService won't wait for the service to show up like getService will.
+    sp<IBinder> service = defaultServiceManager()->checkService(mService);
+    if (service == NULL) {
+        ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
+        return NAME_NOT_FOUND;
+    }
+
+    // Create pipe
+    Fpipe dumpPipe;
+    if (!dumpPipe.init()) {
+        ALOGW("[%s] failed to setup pipe", this->name.string());
+        return -errno;
+    }
+
+    // Run dumping thread
+    const uint64_t start = Nanotime();
+    std::thread worker([&]() {
+        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
+        signal(SIGPIPE, sigpipe_handler);
+        status_t err = service->dump(dumpPipe.writeFd().get(), mArgs);
+        if (err != OK) {
+            ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
+        }
+        dumpPipe.writeFd().reset();
+    });
+
+    // Collect dump content
+    std::string content;
+    bool success = ReadFdToString(dumpPipe.readFd(), &content);
+    worker.join(); // Wait for worker to finish
+    dumpPipe.readFd().reset();
+    if (!success) {
+        ALOGW("[%s] failed to read data from pipe", this->name.string());
+        return -1;
+    }
+
+    ProtoOutputStream proto;
+    proto.write(util::TextDumpProto::COMMAND, std::string(name.string()));
+    proto.write(util::TextDumpProto::CONTENT, content);
+    proto.write(util::TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));
+
+    if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        return EPIPE;
+    }
+    return OK;
+}
+
+// ================================================================================
 // initialization only once in Section.cpp.
 map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
 
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0bb9da9..6162b3a 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -112,7 +112,8 @@
 };
 
 /**
- * Section that calls dumpsys on a system service.
+ * Section that calls protobuf dumpsys on a system service, usually
+ * "dumpsys [service_name] --proto".
  */
 class DumpsysSection : public WorkerThreadSection {
 public:
@@ -127,6 +128,21 @@
 };
 
 /**
+ * Section that calls text dumpsys on a system service, usually "dumpsys [service_name]".
+ */
+class TextDumpsysSection : public WorkerThreadSection {
+public:
+    TextDumpsysSection(int id, const char* service, ...);
+    virtual ~TextDumpsysSection();
+
+    virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
+
+private:
+    String16 mService;
+    Vector<String16> mArgs;
+};
+
+/**
  * Section that calls dumpsys on a system service.
  */
 class SystemPropertyDumpsysSection : public WorkerThreadSection {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1b0e51e..2575542 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -397,7 +397,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10075
+    // Next: 10076
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -479,6 +479,7 @@
         PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
                 10073 [(module) = "framework"];
         GnssStats gnss_stats = 10074 [(module) = "framework"];
+        AppFeaturesOps app_features_ops = 10075 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -7548,6 +7549,51 @@
 }
 
 /**
+ * Historical app ops data per package and features.
+ */
+message AppFeaturesOps {
+    // Uid of the package requesting the op
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Name of the package performing the op
+    optional string package_name = 2;
+
+    // feature id; provided by developer when accessing related API, limited at 50 chars by API.
+    // Features must be provided through manifest using <feature> tag available in R and above.
+    optional string feature_id = 3;
+
+    // operation id; maps to the OPSTR_* constants in AppOpsManager.java
+    optional string op = 4;
+
+    // The number of times the op was granted while the app was in the
+    // foreground (only for trusted requests)
+    optional int64 trusted_foreground_granted_count = 5;
+
+    // The number of times the op was granted while the app was in the
+    // background (only for trusted requests)
+    optional int64 trusted_background_granted_count = 6;
+
+    // The number of times the op was rejected while the app was in the
+    // foreground (only for trusted requests)
+    optional int64 trusted_foreground_rejected_count = 7;
+
+    // The number of times the op was rejected while the app was in the
+    // background (only for trusted requests)
+    optional int64 trusted_background_rejected_count = 8;
+
+    // For long-running operations, total duration of the operation
+    // while the app was in the foreground (only for trusted requests)
+    optional int64 trusted_foreground_duration_millis = 9;
+
+    // For long-running operations, total duration of the operation
+    // while the app was in the background (only for trusted requests)
+    optional int64 trusted_background_duration_millis = 10;
+
+    // Whether AppOps is guarded by Runtime permission
+    optional bool is_runtime_permission = 11;
+}
+
+/**
  * Location Manager API Usage information(e.g. API under usage,
  * API call's parameters).
  * Logged from:
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 7212be8..3f2ec44 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -63,6 +64,7 @@
      * string takes two arguments, in this order: the
      * {@link #getId()} of the original notification channel, and the
      * {@link ShortcutInfo#getId() id} of the conversation.
+     * @hide
      */
     public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
 
@@ -554,8 +556,8 @@
     }
 
     /**
-     * Sets this channel as being person-centric. Different settings and functionality may be
-     * exposed for people-centric channels.
+     * Sets this channel as being converastion-centric. Different settings and functionality may be
+     * exposed for conversation-centric channels.
      *
      * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
      *                        this type would be posted to in absence of a specific conversation id.
@@ -564,8 +566,8 @@
      * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
      *                       channel's conversation.
      */
-    public void setConversationId(@Nullable String parentChannelId,
-            @Nullable String conversationId) {
+    public void setConversationId(@NonNull String parentChannelId,
+            @NonNull String conversationId) {
         mParentId = parentChannelId;
         mConversationId = conversationId;
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 88edb05..cbbdf63 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1573,6 +1573,15 @@
                 PRIORITY_CATEGORY_CONVERSATIONS,
         };
 
+        /** @hide */
+        @IntDef(prefix = { "PRIORITY_SENDERS_" }, value = {
+                PRIORITY_SENDERS_ANY,
+                PRIORITY_SENDERS_CONTACTS,
+                PRIORITY_SENDERS_STARRED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PrioritySenders {}
+
         /** Any sender is prioritized. */
         public static final int PRIORITY_SENDERS_ANY = 0;
         /** Saved contacts are prioritized. */
@@ -1816,8 +1825,9 @@
          * @param suppressedVisualEffects which visual interruptions should be suppressed from
          *                                notifications that are filtered by DND.
          */
-        public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
-                int suppressedVisualEffects, int priorityConversationSenders) {
+        public Policy(int priorityCategories, @PrioritySenders int priorityCallSenders,
+                @PrioritySenders int priorityMessageSenders,
+                int suppressedVisualEffects, @ConversationSenders int priorityConversationSenders) {
             this(priorityCategories, priorityCallSenders, priorityMessageSenders,
                     suppressedVisualEffects, STATE_UNSET, priorityConversationSenders);
         }
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index c389b1a..dd434b4 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -28,5 +28,5 @@
 oneway interface IInlineSuggestionRenderService {
     void renderSuggestion(in IInlineSuggestionUiCallback callback,
                           in InlinePresentation presentation, int width, int height,
-                          in IBinder hostInputToken);
+                          in IBinder hostInputToken, int displayId);
 }
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 29069e7..17e0456 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -23,14 +23,18 @@
 import android.annotation.TestApi;
 import android.app.Service;
 import android.app.slice.Slice;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
@@ -61,7 +65,8 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
-            InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+            InlinePresentation presentation, int width, int height, IBinder hostInputToken,
+            int displayId) {
         if (hostInputToken == null) {
             try {
                 callback.onError();
@@ -70,8 +75,17 @@
             }
             return;
         }
-        final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
-                hostInputToken);
+
+        final DisplayManager displayManager = getSystemService(DisplayManager.class);
+        final Display targetDisplay = displayManager.getDisplay(displayId);
+        if (targetDisplay == null) {
+            sendResult(callback, /*surface*/ null);
+            return;
+        }
+        final Context displayContext = createDisplayContext(targetDisplay);
+
+        final SurfaceControlViewHost host = new SurfaceControlViewHost(displayContext,
+                displayContext.getDisplay(), hostInputToken);
         final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
 
         final View suggestionView = onRenderSuggestion(presentation, width, height);
@@ -90,6 +104,11 @@
                 new WindowManager.LayoutParams(width, height,
                         WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
         host.addView(suggestionRoot, lp);
+        sendResult(callback, surface);
+    }
+
+    private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
+            @Nullable SurfaceControl surface) {
         try {
             callback.onContent(surface);
         } catch (RemoteException e) {
@@ -105,11 +124,11 @@
                 @Override
                 public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
                         @NonNull InlinePresentation presentation, int width, int height,
-                        @Nullable IBinder hostInputToken) {
+                        @Nullable IBinder hostInputToken, int displayId) {
                     mHandler.sendMessage(obtainMessage(
                             InlineSuggestionRenderService::handleRenderSuggestion,
                             InlineSuggestionRenderService.this, callback, presentation,
-                            width, height, hostInputToken));
+                            width, height, hostInputToken, displayId));
                 }
             }.asBinder();
         }
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 680a878..4badede 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -111,6 +111,7 @@
             @NonNull WindowlessWindowManager wwm) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm);
+        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
@@ -135,6 +136,7 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
+        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 73707ca..fb7c04a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -392,7 +392,7 @@
                  * This gets called on a RenderThread worker thread, so members accessed here must
                  * be protected by a lock.
                  */
-                final boolean useBLAST = WindowManagerGlobal.useBLAST();
+                final boolean useBLAST = viewRoot.useBLAST();
                 viewRoot.registerRtFrameCallback(frame -> {
                     try {
                         final SurfaceControl.Transaction t = useBLAST ?
@@ -930,7 +930,7 @@
                                     mSurfaceHeight);
                         }
                     } else if ((layoutSizeChanged || positionChanged) &&
-                            WindowManagerGlobal.useBLAST()) {
+                            viewRoot.useBLAST()) {
                         viewRoot.setUseBLASTSyncTransaction();
                     }
                     mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
@@ -1132,9 +1132,8 @@
 
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
-        if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) {
-            final ViewRootImpl viewRoot = getViewRootImpl();
-
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
             t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
                     frameNumber);
         }
@@ -1150,8 +1149,8 @@
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        final boolean useBLAST = WindowManagerGlobal.useBLAST();
         final ViewRootImpl viewRoot = getViewRootImpl();
+        final boolean useBLAST = viewRoot.useBLAST();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
 
@@ -1211,7 +1210,8 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            boolean useBLAST = WindowManagerGlobal.useBLAST();
+            final ViewRootImpl viewRoot = getViewRootImpl();
+            boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
@@ -1222,8 +1222,6 @@
                 return;
             }
 
-            final ViewRootImpl viewRoot = getViewRootImpl();
-
             final SurfaceControl.Transaction t = useBLAST ?
                 (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
                 mRtTransaction;
@@ -1646,7 +1644,7 @@
     }
 
     private void updateScreenMatrixForEmbeddedHierarchy() {
-        getBoundsOnScreen(mTmpRect, true);
+        getBoundsOnScreen(mTmpRect);
         mTmpMatrix.reset();
         mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
         mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 148b327..4a093e6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -322,7 +322,7 @@
      */
     private boolean mForceNextConfigUpdate;
 
-    private final boolean mUseBLASTAdapter;
+    private boolean mUseBLASTAdapter;
 
     /**
      * Signals that compatibility booleans have been initialized according to
@@ -9639,4 +9639,16 @@
     public void onDescendantUnbufferedRequested() {
         mUnbufferedInputSource = mView.mUnbufferedInputSource;
     }
+
+    /**
+     * Force disabling use of the BLAST adapter regardless of the system
+     * flag. Needs to be called before addView.
+     */
+    void forceDisableBLAST() {
+        mUseBLASTAdapter = false;
+    }
+
+    boolean useBLAST() {
+        return mUseBLASTAdapter;
+    }
 }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d40f505..6435b42 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,11 @@
 
 package android.view;
 
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -67,10 +72,6 @@
 
     private IBinder mDefaultToken;
 
-    private boolean mIsViewAdded;
-    private View mLastView;
-    private WindowManager.LayoutParams mLastParams;
-
     public WindowManagerImpl(Context context) {
         this(context, null);
     }
@@ -102,9 +103,6 @@
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         applyDefaultToken(params);
         mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow);
-        mIsViewAdded = true;
-        mLastView = view;
-        mLastParams = (WindowManager.LayoutParams) params;
     }
 
     @Override
@@ -247,21 +245,19 @@
     }
 
     private WindowInsets computeWindowInsets() {
-        // TODO(window-context): This can only be properly implemented
+        // TODO(b/118118435): This can only be properly implemented
         //  once we flip the new insets mode flag.
-        if (mParentWindow != null) {
-            if (mParentWindow.getDecorView().isAttachedToWindow()) {
-                return mParentWindow.getDecorView().getViewRootImpl()
-                        .getWindowInsets(true /* forceConstruct */);
-            }
-            return getWindowInsetsFromServer(mParentWindow.getAttributes());
-        }
-        if (mIsViewAdded) {
-            return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */);
-        } else {
-            return getWindowInsetsFromServer(new WindowManager.LayoutParams());
-        }
+        // Initialize params which used for obtaining all system insets.
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+        params.token = (mParentWindow != null) ? mParentWindow.getContext().getActivityToken()
+                : mContext.getActivityToken();
+        params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+        params.setFitInsetsTypes(0);
+        params.setFitInsetsSides(0);
 
+        return getWindowInsetsFromServer(params);
     }
 
     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 8caf5b7..ab5a06e 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.graphics.Point;
 import android.util.Size;
 
 /**
@@ -40,6 +41,30 @@
 
     /**
      * Returns the size of the window.
+     * <p>
+     * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b>
+     * This method reports the window size including all system bars area, while
+     * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout
+     * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using:
+     * <pre class="prettyprint">
+     * final WindowMetrics metrics = windowManager.getCurrentMetrics();
+     * // Gets all excluding insets
+     * final WindowInsets windowInsets = metrics.getWindowInsets();
+     * Insets insets = windowInsets.getInsets(WindowInsets.Type.navigationBars());
+     * final DisplayCutout cutout = windowInsets.getCutout();
+     * if (cutout != null) {
+     *     final Insets cutoutSafeInsets = Insets.of(cutout.getSafeInsetsLeft(), ...);
+     *     insets = insets.max(insets, cutoutSafeInsets);
+     * }
+     *
+     * int insetsWidth = insets.right + insets.left;
+     * int insetsHeight = insets.top + insets.bottom;
+     *
+     * // Legacy size that Display#getSize reports
+     * final Size legacySize = new Size(metrics.getWidth() - insetsWidth,
+     *         metrics.getHeight() - insetsHeight);
+     * </pre>
+     * </p>
      *
      * @return window size in pixel.
      */
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 5700dda..e50da40 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -22,7 +22,9 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.LocaleList;
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.Display;
 import android.view.inline.InlinePresentationSpec;
 
 import com.android.internal.util.DataClass;
@@ -67,7 +69,11 @@
      */
     private @NonNull LocaleList mSupportedLocales;
 
-    // TODO(b/149609075): the generated code needs to be manually fixed due to the bug.
+    /**
+     * The extras state propagated from the IME to pass extra data.
+     */
+    private @Nullable Bundle mExtras;
+
     /**
      * The host input token of the IME that made the request. This will be set by the system for
      * safety reasons.
@@ -77,9 +83,12 @@
     private @Nullable IBinder mHostInputToken;
 
     /**
-     * The extras state propagated from the IME to pass extra data.
+     * The host display id of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
      */
-    private @Nullable Bundle mExtras;
+    private int mHostDisplayId;
 
     /**
      * @hide
@@ -89,6 +98,24 @@
         mHostInputToken = hostInputToken;
     }
 
+    // TODO(b/149609075): remove once IBinder parcelling is natively supported
+    private void parcelHostInputToken(@NonNull Parcel parcel, int flags) {
+        parcel.writeStrongBinder(mHostInputToken);
+    }
+
+    // TODO(b/149609075): remove once IBinder parcelling is natively supported
+    private @Nullable IBinder unparcelHostInputToken(Parcel parcel) {
+        return parcel.readStrongBinder();
+    }
+
+    /**
+     * @hide
+     * @see {@link #mHostDisplayId}.
+     */
+    public void setHostDisplayId(int hostDisplayId) {
+        mHostDisplayId = hostDisplayId;
+    }
+
     private void onConstructed() {
         Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
     }
@@ -111,10 +138,17 @@
     }
 
     @Nullable
+    private static int defaultHostDisplayId() {
+        return Display.INVALID_DISPLAY;
+    }
+
+    @Nullable
     private static Bundle defaultExtras() {
         return null;
     }
 
+
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
@@ -122,6 +156,8 @@
         abstract Builder setHostPackageName(@Nullable String value);
 
         abstract Builder setHostInputToken(IBinder hostInputToken);
+
+        abstract Builder setHostDisplayId(int value);
     }
 
 
@@ -145,8 +181,9 @@
             @NonNull List<InlinePresentationSpec> presentationSpecs,
             @NonNull String hostPackageName,
             @NonNull LocaleList supportedLocales,
+            @Nullable Bundle extras,
             @Nullable IBinder hostInputToken,
-            @Nullable Bundle extras) {
+            int hostDisplayId) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
@@ -157,8 +194,9 @@
         this.mSupportedLocales = supportedLocales;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSupportedLocales);
-        this.mHostInputToken = hostInputToken;
         this.mExtras = extras;
+        this.mHostInputToken = hostInputToken;
+        this.mHostDisplayId = hostDisplayId;
 
         onConstructed();
     }
@@ -202,6 +240,14 @@
     }
 
     /**
+     * The extras state propagated from the IME to pass extra data.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
      * The host input token of the IME that made the request. This will be set by the system for
      * safety reasons.
      *
@@ -213,11 +259,14 @@
     }
 
     /**
-     * The extras state propagated from the IME to pass extra data.
+     * The host display id of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
      */
     @DataClass.Generated.Member
-    public @Nullable Bundle getExtras() {
-        return mExtras;
+    public int getHostDisplayId() {
+        return mHostDisplayId;
     }
 
     @Override
@@ -231,8 +280,9 @@
                 "presentationSpecs = " + mPresentationSpecs + ", " +
                 "hostPackageName = " + mHostPackageName + ", " +
                 "supportedLocales = " + mSupportedLocales + ", " +
+                "extras = " + mExtras + ", " +
                 "hostInputToken = " + mHostInputToken + ", " +
-                "extras = " + mExtras +
+                "hostDisplayId = " + mHostDisplayId +
         " }";
     }
 
@@ -253,8 +303,9 @@
                 && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
                 && java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
                 && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales)
+                && java.util.Objects.equals(mExtras, that.mExtras)
                 && java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
-                && java.util.Objects.equals(mExtras, that.mExtras);
+                && mHostDisplayId == that.mHostDisplayId;
     }
 
     @Override
@@ -268,27 +319,29 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
         _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales);
-        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         _hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
+        _hash = 31 * _hash + mHostDisplayId;
         return _hash;
     }
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mHostInputToken != null) flg |= 0x10;
-        if (mExtras != null) flg |= 0x20;
+        if (mExtras != null) flg |= 0x10;
+        if (mHostInputToken != null) flg |= 0x20;
         dest.writeByte(flg);
         dest.writeInt(mMaxSuggestionCount);
         dest.writeParcelableList(mPresentationSpecs, flags);
         dest.writeString(mHostPackageName);
         dest.writeTypedObject(mSupportedLocales, flags);
-        if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
         if (mExtras != null) dest.writeBundle(mExtras);
+        parcelHostInputToken(dest, flags);
+        dest.writeInt(mHostDisplayId);
     }
 
     @Override
@@ -298,7 +351,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ InlineSuggestionsRequest(@NonNull android.os.Parcel in) {
+    /* package-private */ InlineSuggestionsRequest(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -308,8 +361,9 @@
         in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
         String hostPackageName = in.readString();
         LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
-        IBinder hostInputToken = (flg & 0x10) == 0 ? null : in.readStrongBinder();
-        Bundle extras = (flg & 0x20) == 0 ? null : in.readBundle();
+        Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle();
+        IBinder hostInputToken = unparcelHostInputToken(in);
+        int hostDisplayId = in.readInt();
 
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
@@ -321,8 +375,9 @@
         this.mSupportedLocales = supportedLocales;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSupportedLocales);
-        this.mHostInputToken = hostInputToken;
         this.mExtras = extras;
+        this.mHostInputToken = hostInputToken;
+        this.mHostDisplayId = hostDisplayId;
 
         onConstructed();
     }
@@ -336,7 +391,7 @@
         }
 
         @Override
-        public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) {
+        public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) {
             return new InlineSuggestionsRequest(in);
         }
     };
@@ -352,8 +407,9 @@
         private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
         private @NonNull String mHostPackageName;
         private @NonNull LocaleList mSupportedLocales;
-        private @Nullable IBinder mHostInputToken;
         private @Nullable Bundle mExtras;
+        private @Nullable IBinder mHostInputToken;
+        private int mHostDisplayId;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -436,6 +492,17 @@
         }
 
         /**
+         * The extras state propagated from the IME to pass extra data.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setExtras(@Nullable Bundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mExtras = value;
+            return this;
+        }
+
+        /**
          * The host input token of the IME that made the request. This will be set by the system for
          * safety reasons.
          *
@@ -445,26 +512,30 @@
         @Override
         @NonNull Builder setHostInputToken(@Nullable IBinder value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10;
+            mBuilderFieldsSet |= 0x20;
             mHostInputToken = value;
             return this;
         }
 
         /**
-         * The extras state propagated from the IME to pass extra data.
+         * The host display id of the IME that made the request. This will be set by the system for
+         * safety reasons.
+         *
+         * @hide
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setExtras(@Nullable Bundle value) {
+        @Override
+        @NonNull Builder setHostDisplayId(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
-            mExtras = value;
+            mBuilderFieldsSet |= 0x40;
+            mHostDisplayId = value;
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -476,23 +547,27 @@
                 mSupportedLocales = defaultSupportedLocales();
             }
             if ((mBuilderFieldsSet & 0x10) == 0) {
-                mHostInputToken = defaultHostInputToken();
+                mExtras = defaultExtras();
             }
             if ((mBuilderFieldsSet & 0x20) == 0) {
-                mExtras = defaultExtras();
+                mHostInputToken = defaultHostInputToken();
+            }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
+                mHostDisplayId = defaultHostDisplayId();
             }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
                     mPresentationSpecs,
                     mHostPackageName,
                     mSupportedLocales,
+                    mExtras,
                     mHostInputToken,
-                    mExtras);
+                    mHostDisplayId);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -500,10 +575,10 @@
     }
 
     @DataClass.Generated(
-            time = 1581747892762L,
+            time = 1582339908980L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate @android.annotation.Nullable android.os.Bundle mExtras\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 7d1cf5d..d6687f0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -58,6 +58,7 @@
 import "frameworks/base/core/proto/android/service/usb.proto";
 import "frameworks/base/core/proto/android/util/event_log_tags.proto";
 import "frameworks/base/core/proto/android/util/log.proto";
+import "frameworks/base/core/proto/android/util/textdump.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/section.proto";
 import "frameworks/base/proto/src/ipconnectivity.proto";
@@ -510,6 +511,17 @@
         (section).args = "sensorservice --proto"
     ];
 
+    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
+    optional android.util.TextDumpProto textdump_wifi = 4000 [
+        (section).type = SECTION_TEXT_DUMPSYS,
+        (section).args = "wifi"
+    ];
+
+    optional android.util.TextDumpProto textdump_bluetooth = 4001 [
+        (section).type = SECTION_TEXT_DUMPSYS,
+        (section).args = "bluetooth_manager"
+    ];
+
     // Reserved for OEMs.
     extensions 50000 to 100000;
 }
diff --git a/core/proto/android/section.proto b/core/proto/android/section.proto
index 5afe22a..299d6f9a 100644
--- a/core/proto/android/section.proto
+++ b/core/proto/android/section.proto
@@ -46,6 +46,10 @@
 
     // incidentd calls tombstoned for annotated field
     SECTION_TOMBSTONE = 6;
+
+    // incidentd calls legacy text dumpsys for annotated field. The section will only be generated
+    // on userdebug and eng builds.
+    SECTION_TEXT_DUMPSYS = 7;
 }
 
 message SectionFlags {
diff --git a/core/proto/android/util/textdump.proto b/core/proto/android/util/textdump.proto
new file mode 100644
index 0000000..6118487
--- /dev/null
+++ b/core/proto/android/util/textdump.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.util;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message TextDumpProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    // The command that was executed
+    optional string command = 1;
+    // The content that was dumped
+    optional string content = 2;
+    // The duration of the dump process
+    optional int64 dump_duration_ns = 3;
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 718ca46..b42fce0 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,6 +138,9 @@
     <!-- vr test permissions -->
     <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
 
+    <!-- WindowMetricsTest permissions -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
     <application android:theme="@style/Theme" android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
new file mode 100644
index 0000000..fa68860
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 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 static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.util.Size;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link WindowManager#getCurrentWindowMetrics()} and
+ * {@link WindowManager#getMaximumWindowMetrics()}.
+ *
+ * <p>Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowMetricsTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowMetricsTest {
+    private Context mWindowContext;
+    private WindowManager mWm;
+
+    @Before
+    public void setUp() {
+        final Context insetContext = InstrumentationRegistry.getInstrumentation()
+                .getTargetContext();
+        final Display display = insetContext.getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY);
+        mWindowContext = insetContext.createDisplayContext(display)
+                .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+        mWm = mWindowContext.getSystemService(WindowManager.class);
+    }
+
+    @Test
+    public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
+        final View view = new View(mWindowContext);
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+        Handler.getMain().runWithScissors(() -> {
+            mWm.addView(view, params);
+            // Check get metrics do not crash.
+            WindowMetrics currentMetrics = mWm.getCurrentWindowMetrics();
+            WindowMetrics maxMetrics = mWm.getMaximumWindowMetrics();
+            verifyMetricsSanity(currentMetrics, maxMetrics);
+
+            mWm.removeViewImmediate(view);
+            // Check get metrics do not crash.
+            currentMetrics = mWm.getCurrentWindowMetrics();
+            maxMetrics = mWm.getMaximumWindowMetrics();
+            verifyMetricsSanity(currentMetrics, maxMetrics);
+        }, 0);
+    }
+
+    private static void verifyMetricsSanity(WindowMetrics currentMetrics,
+            WindowMetrics maxMetrics) {
+        Size currentSize = currentMetrics.getSize();
+        Size maxSize = maxMetrics.getSize();
+
+        assertTrue(maxSize.getWidth() >= currentSize.getWidth());
+        assertTrue(maxSize.getHeight() >= currentSize.getHeight());
+    }
+}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index c6905ec..59681e9 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -41,7 +41,7 @@
         "framework-minus-apex",
         "ext",
         "framework-res",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-module_libs_api",
         "framework-telephony-stubs",
         "android.test.runner",
         "android.test.base",
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index cff5504..c5a5d34 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -255,6 +255,10 @@
     // Inform the user a foreground service while-in-use permission is restricted.
     NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61;
 
+    // Display the Android Debug Protocol status
+    // Package: android
+    NOTE_ADB_WIFI_ACTIVE = 62;
+
     // 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/api/current.txt b/services/api/current.txt
index 4a0a0d8..8c90165 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -49,14 +49,15 @@
   public abstract class SystemService {
     ctor public SystemService(@NonNull android.content.Context);
     method @NonNull public final android.content.Context getContext();
-    method public boolean isSupportedUser(@NonNull com.android.server.SystemService.TargetUser);
+    method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser);
     method public void onBootPhase(int);
-    method public void onCleanupUser(@NonNull com.android.server.SystemService.TargetUser);
     method public abstract void onStart();
-    method public void onStartUser(@NonNull com.android.server.SystemService.TargetUser);
-    method public void onStopUser(@NonNull com.android.server.SystemService.TargetUser);
-    method public void onSwitchUser(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
-    method public void onUnlockUser(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser);
     method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder);
     method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean);
     field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 5405fc7..e49c1ed 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -331,7 +331,7 @@
     }
 
     @Override // from SystemService
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 5d5af53..7ad5632 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -86,9 +86,9 @@
      */
     public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
             @NonNull InlinePresentation presentation, int width, int height,
-            @Nullable IBinder hostInputToken) {
-        scheduleAsyncRequest(
-                (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
+            @Nullable IBinder hostInputToken, int displayId) {
+        scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height,
+                hostInputToken, displayId));
     }
 
     @Nullable
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 0d8c89b..56e8707 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -134,7 +134,7 @@
         if (inlineAuthentication != null) {
             InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication,
                     remoteRenderService, onClickFactory, onErrorCallback,
-                    request.getHostInputToken());
+                    request.getHostInputToken(), request.getHostDisplayId());
             inlineSuggestions.add(inlineAuthSuggestion);
 
             return new InlineSuggestionsResponse(inlineSuggestions);
@@ -164,7 +164,7 @@
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
                     fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation),
                     onClickFactory, remoteRenderService, onErrorCallback,
-                    request.getHostInputToken());
+                    request.getHostInputToken(), request.getHostDisplayId());
 
             inlineSuggestions.add(inlineSuggestion);
         }
@@ -172,7 +172,8 @@
             for (InlinePresentation inlinePresentation : inlineActions) {
                 final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
                         mergedInlinePresentation(request, 0, inlinePresentation),
-                        remoteRenderService, onErrorCallback, request.getHostInputToken());
+                        remoteRenderService, onErrorCallback, request.getHostInputToken(),
+                        request.getHostDisplayId());
                 inlineSuggestions.add(inlineAction);
             }
         }
@@ -215,7 +216,8 @@
             @NonNull Context context,
             @NonNull InlinePresentation inlinePresentation,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
-            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+            int displayId) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
@@ -227,7 +229,7 @@
         };
         return new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
-                        remoteRenderService, hostInputToken));
+                        remoteRenderService, hostInputToken, displayId));
     }
 
     private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@@ -235,7 +237,8 @@
             @NonNull InlinePresentation inlinePresentation,
             @NonNull BiConsumer<Dataset, Integer> onClickFactory,
             @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
-            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+            int displayId) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
@@ -246,7 +249,7 @@
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation,
                         () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
-                        remoteRenderService, hostInputToken));
+                        remoteRenderService, hostInputToken, displayId));
 
         return inlineSuggestion;
     }
@@ -255,7 +258,7 @@
             @NonNull InlinePresentation inlinePresentation,
             @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
             @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
-            @Nullable IBinder hostInputToken) {
+            @Nullable IBinder hostInputToken, int displayId) {
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
                 InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
@@ -264,7 +267,7 @@
                 createInlineContentProvider(inlinePresentation,
                         () -> onClickFactory.accept(null,
                                 AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
-                        onErrorCallback, remoteRenderService, hostInputToken));
+                        onErrorCallback, remoteRenderService, hostInputToken, displayId));
     }
 
     /**
@@ -291,7 +294,8 @@
             @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
             @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
-            @Nullable IBinder hostInputToken) {
+            @Nullable IBinder hostInputToken,
+            int displayId) {
         return new IInlineContentProvider.Stub() {
             @Override
             public void provideContent(int width, int height, IInlineContentCallback callback) {
@@ -305,7 +309,7 @@
                     }
 
                     remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
-                            width, height, hostInputToken);
+                            width, height, hostInputToken, displayId);
                 });
             }
         };
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 9a33fc9..5d2b9f3 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -201,7 +201,7 @@
     }
 
     @Override // from SystemService
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d86b223..b1cb138 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -279,7 +279,7 @@
         }
 
         @Override
-        public void onStartUser(TargetUser user) {
+        public void onUserStarting(TargetUser user) {
             mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
         }
     }
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index aabe98b..e5c0540 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -238,12 +238,12 @@
      * <p>By default returns {@code true}, but subclasses should extend for optimization, if they
      * don't support some types (like headless system user).
      */
-    public boolean isSupportedUser(@NonNull TargetUser user) {
+    public boolean isUserSupported(@NonNull TargetUser user) {
         return true;
     }
 
     /**
-     * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}.
+     * Helper method used to dump which users are {@link #onUserStarting(TargetUser) supported}.
      *
      * @hide
      */
@@ -264,7 +264,7 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -273,7 +273,7 @@
     public void onStartUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -287,17 +287,17 @@
      * Called when a new user is starting, for system services to initialize any per-user
      * state they maintain for running users.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * @param user target user
      */
-    public void onStartUser(@NonNull TargetUser user) {
+    public void onUserStarting(@NonNull TargetUser user) {
         onStartUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -306,7 +306,7 @@
     public void onUnlockUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -326,19 +326,30 @@
      * the user will transition into the {@code STATE_RUNNING_UNLOCKED} state.
      * Code written inside system services should use
      * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
-     * these states.
+     * these states, or use {@link #onUserUnlocked(TargetUser)} instead.
      * <p>
-     * This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * @param user target user
      */
-    public void onUnlockUser(@NonNull TargetUser user) {
+    public void onUserUnlocking(@NonNull TargetUser user) {
         onUnlockUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+     * Called after an existing user is unlocked.
+     *
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
+     * this user.
+     *
+     * @param user target user
+     */
+    public void onUserUnlocked(@NonNull TargetUser user) {
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -347,7 +358,7 @@
     public void onSwitchUser(@UserIdInt int toUserId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -362,7 +373,7 @@
      * special behavior for whichever user is currently in the foreground.  This is called
      * before any application processes are aware of the new user.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * either of the users ({@code from} or {@code to}).
      *
      * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
@@ -371,12 +382,12 @@
      * @param from the user switching from
      * @param to the user switching to
      */
-    public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) {
+    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
         onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -385,7 +396,7 @@
     public void onStopUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -402,19 +413,19 @@
      * broadcast to the user; it is a good place to stop making use of any resources of that
      * user (such as binding to a service running in the user).
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
      *
      * @param user target user
      */
-    public void onStopUser(@NonNull TargetUser user) {
+    public void onUserStopping(@NonNull TargetUser user) {
         onStopUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -423,7 +434,7 @@
     public void onCleanupUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -434,20 +445,16 @@
     }
 
     /**
-     * Called when an existing user is stopping, for system services to finalize any per-user
-     * state they maintain for running users.  This is called after all application process
-     * teardown of the user is complete.
+     * Called after an existing user is stopped.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This is called after all application process teardown of the user is complete.
+     *
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
-     * <p>NOTE: When this callback is called, the CE storage for the target user may not be
-     * accessible already.  Use {@link #onStopUser(TargetUser)} instead if you need to access the CE
-     * storage.
-     *
      * @param user target user
      */
-    public void onCleanupUser(@NonNull TargetUser user) {
+    public void onUserStopped(@NonNull TargetUser user) {
         onCleanupUser(user.getUserInfo());
     }
 
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index e7f7846..f16f6ef 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -51,7 +51,8 @@
 
     // Constants used on onUser(...)
     private static final String START = "Start";
-    private static final String UNLOCK = "Unlock";
+    private static final String UNLOCKING = "Unlocking";
+    private static final String UNLOCKED = "Unlocked";
     private static final String SWITCH = "Switch";
     private static final String STOP = "Stop";
     private static final String CLEANUP = "Cleanup";
@@ -260,7 +261,14 @@
      * Unlocks the given user.
      */
     public void unlockUser(final @UserIdInt int userHandle) {
-        onUser(UNLOCK, userHandle);
+        onUser(UNLOCKING, userHandle);
+    }
+
+    /**
+     * Called after the user was unlocked.
+     */
+    public void onUserUnlocked(final @UserIdInt int userHandle) {
+        onUser(UNLOCKED, userHandle);
     }
 
     /**
@@ -304,12 +312,12 @@
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
             final String serviceName = service.getClass().getName();
-            boolean supported = service.isSupportedUser(curUser);
+            boolean supported = service.isUserSupported(curUser);
 
             // Must check if either curUser or prevUser is supported (for example, if switching from
             // unsupported to supported, we still need to notify the services)
             if (!supported && prevUser != null) {
-                supported = service.isSupportedUser(prevUser);
+                supported = service.isUserSupported(prevUser);
             }
 
             if (!supported) {
@@ -328,19 +336,22 @@
             try {
                 switch (onWhat) {
                     case SWITCH:
-                        service.onSwitchUser(prevUser, curUser);
+                        service.onUserSwitching(prevUser, curUser);
                         break;
                     case START:
-                        service.onStartUser(curUser);
+                        service.onUserStarting(curUser);
                         break;
-                    case UNLOCK:
-                        service.onUnlockUser(curUser);
+                    case UNLOCKING:
+                        service.onUserUnlocking(curUser);
+                        break;
+                    case UNLOCKED:
+                        service.onUserUnlocked(curUser);
                         break;
                     case STOP:
-                        service.onStopUser(curUser);
+                        service.onUserStopping(curUser);
                         break;
                     case CLEANUP:
-                        service.onCleanupUser(curUser);
+                        service.onUserStopped(curUser);
                         break;
                     default:
                         throw new IllegalArgumentException(onWhat + " what?");
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
index 74f113f..9a9374c 100644
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -26,6 +26,7 @@
 
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -45,7 +46,7 @@
             "sys.userspace_reboot.log.last_started";
     private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
             "sys.userspace_reboot.log.last_finished";
-    private static final String BOOT_REASON_PROPERTY = "sys.boot.reason";
+    private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last";
 
     private UserspaceRebootLogger() {}
 
@@ -111,26 +112,28 @@
         if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
             return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
         }
-        String reason = SystemProperties.get(BOOT_REASON_PROPERTY, "");
+        String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, ""));
         if (reason.startsWith("reboot,")) {
             reason = reason.substring("reboot".length());
         }
-        switch (reason) {
-            case "userspace_failed,watchdog_fork":
-                // Since fork happens before shutdown sequence, attribute it to
-                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED.
-            case "userspace_failed,shutdown_aborted":
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-            case "userspace_failed,init_user0_failed":
-                // init_user0 will fail if userdata wasn't remounted correctly, attribute to
-                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT.
-            case "mount_userdata_failed":
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-            case "userspace_failed,watchdog_triggered":
-                return
-                    USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
-            default:
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+        if (reason.startsWith("userspace_failed,watchdog_fork")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
         }
+        if (reason.startsWith("userspace_failed,shutdown_aborted")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+        }
+        if (reason.startsWith("mount_userdata_failed")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,init_user0")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,enablefilecrypto")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,watchdog_triggered")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+        }
+        return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
     }
 }
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f16e3ce..6c68c31 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -20,19 +20,36 @@
 
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbProtoEnums;
 import android.debug.AdbTransportType;
+import android.debug.PairDevice;
+import android.net.ConnectivityManager;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
 import android.net.Uri;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -51,6 +68,8 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
@@ -71,6 +90,8 @@
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -79,6 +100,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
@@ -87,6 +109,7 @@
 public class AdbDebuggingManager {
     private static final String TAG = "AdbDebuggingManager";
     private static final boolean DEBUG = false;
+    private static final boolean MDNS_DEBUG = false;
 
     private static final String ADBD_SOCKET = "adbd";
     private static final String ADB_DIRECTORY = "misc/adb";
@@ -98,19 +121,39 @@
     private static final int BUFFER_SIZE = 65536;
 
     private final Context mContext;
+    private final ContentResolver mContentResolver;
     private final Handler mHandler;
     private AdbDebuggingThread mThread;
-    private boolean mAdbEnabled = false;
+    private boolean mAdbUsbEnabled = false;
+    private boolean mAdbWifiEnabled = false;
     private String mFingerprints;
-    private final List<String> mConnectedKeys;
+    // A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
+    private final Map<String, Integer> mConnectedKeys;
     private String mConfirmComponent;
     private final File mTestUserKeyFile;
 
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
+            "persist.adb.tls_server.enable";
+    private static final String WIFI_PERSISTENT_GUID =
+            "persist.adb.wifi.guid";
+    private static final int PAIRING_CODE_LENGTH = 6;
+    private PairingThread mPairingThread = null;
+    // A list of keys connected via wifi
+    private final Set<String> mWifiConnectedKeys;
+    // The current info of the adbwifi connection.
+    private AdbConnectionInfo mAdbConnectionInfo;
+    // Polls for a tls port property when adb wifi is enabled
+    private AdbConnectionPortPoller mConnectionPortPoller;
+    private final PortListenerImpl mPortListener = new PortListenerImpl();
+
     public AdbDebuggingManager(Context context) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mTestUserKeyFile = null;
-        mConnectedKeys = new ArrayList<>(1);
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
     }
 
     /**
@@ -121,9 +164,178 @@
     protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mConfirmComponent = confirmComponent;
         mTestUserKeyFile = testUserKeyFile;
-        mConnectedKeys = new ArrayList<>();
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
+    }
+
+    class PairingThread extends Thread implements NsdManager.RegistrationListener {
+        private NsdManager mNsdManager;
+        private String mPublicKey;
+        private String mPairingCode;
+        private String mGuid;
+        private String mServiceName;
+        private final String mServiceType = "_adb_secure_pairing._tcp.";
+        private int mPort;
+
+        private native int native_pairing_start(String guid, String password);
+        private native void native_pairing_cancel();
+        private native boolean native_pairing_wait();
+
+        PairingThread(String pairingCode, String serviceName) {
+            super(TAG);
+            mPairingCode = pairingCode;
+            mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID);
+            mServiceName = serviceName;
+            if (serviceName == null || serviceName.isEmpty()) {
+                mServiceName = mGuid;
+            }
+            mPort = -1;
+            mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+        }
+
+        @Override
+        public void run() {
+            if (mGuid.isEmpty()) {
+                Slog.e(TAG, "adbwifi guid was not set");
+                return;
+            }
+            mPort = native_pairing_start(mGuid, mPairingCode);
+            if (mPort <= 0 || mPort > 65535) {
+                Slog.e(TAG, "Unable to start pairing server");
+                return;
+            }
+
+            // Register the mdns service
+            NsdServiceInfo serviceInfo = new NsdServiceInfo();
+            serviceInfo.setServiceName(mServiceName);
+            serviceInfo.setServiceType(mServiceType);
+            serviceInfo.setPort(mPort);
+            mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
+
+            // Send pairing port to UI
+            Message msg = mHandler.obtainMessage(
+                    AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT);
+            msg.obj = mPort;
+            mHandler.sendMessage(msg);
+
+            boolean paired = native_pairing_wait();
+            if (DEBUG) {
+                if (mPublicKey != null) {
+                    Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
+                } else {
+                    Slog.i(TAG, "Pairing failed");
+                }
+            }
+
+            mNsdManager.unregisterService(this);
+
+            Bundle bundle = new Bundle();
+            bundle.putString("publicKey", paired ? mPublicKey : null);
+            Message message = Message.obtain(mHandler,
+                                             AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT,
+                                             bundle);
+            mHandler.sendMessage(message);
+        }
+
+        public void cancelPairing() {
+            native_pairing_cancel();
+        }
+
+        @Override
+        public void onServiceRegistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.e(TAG, "Failed to register pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+            cancelPairing();
+        }
+
+        @Override
+        public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+        }
+    }
+
+    interface AdbConnectionPortListener {
+        void onPortReceived(int port);
+    }
+
+    /**
+     * This class will poll for a period of time for adbd to write the port
+     * it connected to.
+     *
+     * TODO(joshuaduong): The port is being sent via system property because the adbd socket
+     * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the
+     * port through different means. A better fix would be to always start AdbDebuggingManager, but
+     * it needs to adjust accordingly on whether ro.adb.secure is set.
+     */
+    static class AdbConnectionPortPoller extends Thread {
+        private final String mAdbPortProp = "service.adb.tls.port";
+        private AdbConnectionPortListener mListener;
+        private final int mDurationSecs = 10;
+        private AtomicBoolean mCanceled = new AtomicBoolean(false);
+
+        AdbConnectionPortPoller(AdbConnectionPortListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "Starting adb port property poller");
+            // Once adbwifi is enabled, we poll the service.adb.tls.port
+            // system property until we get the port, or -1 on failure.
+            // Let's also limit the polling to 10 seconds, just in case
+            // something went wrong.
+            for (int i = 0; i < mDurationSecs; ++i) {
+                if (mCanceled.get()) {
+                    return;
+                }
+
+                // If the property is set to -1, then that means adbd has failed
+                // to start the server. Otherwise we should have a valid port.
+                int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
+                if (port == -1 || (port > 0 && port <= 65535)) {
+                    mListener.onPortReceived(port);
+                    return;
+                }
+                SystemClock.sleep(1000);
+            }
+            Slog.w(TAG, "Failed to receive adb connection port");
+            mListener.onPortReceived(-1);
+        }
+
+        public void cancelAndWait() {
+            mCanceled.set(true);
+            if (this.isAlive()) {
+                try {
+                    this.join();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    class PortListenerImpl implements AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            Message msg = mHandler.obtainMessage(port > 0
+                     ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+                     : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+            msg.obj = port;
+            mHandler.sendMessage(msg);
+        }
     }
 
     class AdbDebuggingThread extends Thread {
@@ -213,6 +425,46 @@
                                 AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
                         msg.obj = key;
                         mHandler.sendMessage(msg);
+                    } else if (buffer[0] == 'W' && buffer[1] == 'E') {
+                        // adbd_auth.h and AdbTransportType.aidl need to be kept in
+                        // sync.
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
+                    } else if (buffer[0] == 'W' && buffer[1] == 'F') {
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS disconnect message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
                     } else {
                         Slog.e(TAG, "Wrong message: "
                                 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
@@ -268,7 +520,156 @@
         }
     }
 
+    class AdbConnectionInfo {
+        private String mBssid;
+        private String mSsid;
+        private int mPort;
+
+        AdbConnectionInfo() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+
+        AdbConnectionInfo(String bssid, String ssid) {
+            mBssid = bssid;
+            mSsid = ssid;
+        }
+
+        AdbConnectionInfo(AdbConnectionInfo other) {
+            mBssid = other.mBssid;
+            mSsid = other.mSsid;
+            mPort = other.mPort;
+        }
+
+        public String getBSSID() {
+            return mBssid;
+        }
+
+        public String getSSID() {
+            return mSsid;
+        }
+
+        public int getPort() {
+            return mPort;
+        }
+
+        public void setPort(int port) {
+            mPort = port;
+        }
+
+        public void clear() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+    }
+
+    private void setAdbConnectionInfo(AdbConnectionInfo info) {
+        synchronized (mAdbConnectionInfo) {
+            if (info == null) {
+                mAdbConnectionInfo.clear();
+                return;
+            }
+            mAdbConnectionInfo = info;
+        }
+    }
+
+    private AdbConnectionInfo getAdbConnectionInfo() {
+        synchronized (mAdbConnectionInfo) {
+            return new AdbConnectionInfo(mAdbConnectionInfo);
+        }
+    }
+
     class AdbDebuggingHandler extends Handler {
+        private NotificationManager mNotificationManager;
+        private boolean mAdbNotificationShown;
+
+        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                // We only care about when wifi is disabled, and when there is a wifi network
+                // change.
+                if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                    int state = intent.getIntExtra(
+                            WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+                    if (state == WifiManager.WIFI_STATE_DISABLED) {
+                        Slog.i(TAG, "Wifi disabled. Disabling adbwifi.");
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                    }
+                } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                    // We only care about wifi type connections
+                    NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+                            WifiManager.EXTRA_NETWORK_INFO);
+                    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+                        // Check for network disconnect
+                        if (!networkInfo.isConnected()) {
+                            Slog.i(TAG, "Network disconnected. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                            return;
+                        }
+
+                        WifiManager wifiManager =
+                                (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+                        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+                        if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                            Slog.i(TAG, "Not connected to any wireless network."
+                                    + " Not enabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+
+                        // Check for network change
+                        String bssid = wifiInfo.getBSSID();
+                        if (bssid == null || bssid.isEmpty()) {
+                            Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+                        synchronized (mAdbConnectionInfo) {
+                            if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
+                                Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
+                                Settings.Global.putInt(mContentResolver,
+                                        Settings.Global.ADB_WIFI_ENABLED, 0);
+                            }
+                        }
+                    }
+                }
+            }
+        };
+
+        private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
+
+        private boolean isTv() {
+            return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        }
+
+        private void setupNotifications() {
+            if (mNotificationManager != null) {
+                return;
+            }
+            mNotificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+            if (mNotificationManager == null) {
+                Slog.e(TAG, "Unable to setup notifications for wireless debugging");
+                return;
+            }
+
+            // Ensure that the notification channels are set up
+            if (isTv()) {
+                // TV-specific notification channel
+                mNotificationManager.createNotificationChannel(
+                        new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+                                mContext.getString(
+                                        com.android.internal.R.string
+                                                .adb_debugging_notification_channel_tv),
+                                NotificationManager.IMPORTANCE_HIGH));
+            }
+        }
+
         // The default time to schedule the job to keep the keystore updated with a currently
         // connected key as well as to removed expired keys.
         static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000;
@@ -288,8 +689,50 @@
         static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9;
         static final int MESSAGE_ADB_CONNECTED_KEY = 10;
 
+        // === Messages from the UI ==============
+        // UI asks adbd to enable adbdwifi
+        static final int MSG_ADBDWIFI_ENABLE = 11;
+        // UI asks adbd to disable adbdwifi
+        static final int MSG_ADBDWIFI_DISABLE = 12;
+        // Cancel pairing
+        static final int MSG_PAIRING_CANCEL = 14;
+        // Enable pairing by pairing code
+        static final int MSG_PAIR_PAIRING_CODE = 15;
+        // Enable pairing by QR code
+        static final int MSG_PAIR_QR_CODE = 16;
+        // UI asks to unpair (forget) a device.
+        static final int MSG_REQ_UNPAIR = 17;
+        // User allows debugging on the current network
+        static final int MSG_ADBWIFI_ALLOW = 18;
+        // User denies debugging on the current network
+        static final int MSG_ADBWIFI_DENY = 19;
+
+        // === Messages from the PairingThread ===========
+        // Result of the pairing
+        static final int MSG_RESPONSE_PAIRING_RESULT = 20;
+        // The port opened for pairing
+        static final int MSG_RESPONSE_PAIRING_PORT = 21;
+
+        // === Messages from adbd ================
+        // Notifies us a wifi device connected.
+        static final int MSG_WIFI_DEVICE_CONNECTED = 22;
+        // Notifies us a wifi device disconnected.
+        static final int MSG_WIFI_DEVICE_DISCONNECTED = 23;
+        // Notifies us the TLS server is connected and listening
+        static final int MSG_SERVER_CONNECTED = 24;
+        // Notifies us the TLS server is disconnected
+        static final int MSG_SERVER_DISCONNECTED = 25;
+
+        // === Messages we can send to adbd ===========
+        static final String MSG_DISCONNECT_DEVICE = "DD";
+        static final String MSG_DISABLE_ADBDWIFI = "DA";
+
         private AdbKeyStore mAdbKeyStore;
 
+        // Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
+        // connection unless all transport types are disconnected.
+        private int mAdbEnabledRefCount = 0;
+
         private ContentObserver mAuthTimeObserver = new ContentObserver(this) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
@@ -314,44 +757,111 @@
             mAdbKeyStore = adbKeyStore;
         }
 
+        // Show when at least one device is connected.
+        public void showAdbConnectedNotification(boolean show) {
+            final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
+            final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title;
+            if (show == mAdbNotificationShown) {
+                return;
+            }
+            setupNotifications();
+            if (!mAdbNotificationShown) {
+                Resources r = mContext.getResources();
+                CharSequence title = r.getText(titleRes);
+                CharSequence message = r.getText(
+                        com.android.internal.R.string.adbwifi_active_notification_message);
+
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+                        intent, 0, null, UserHandle.CURRENT);
+
+                Notification notification =
+                        new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+                                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                                .setWhen(0)
+                                .setOngoing(true)
+                                .setTicker(title)
+                                .setDefaults(0)  // please be quiet
+                                .setColor(mContext.getColor(
+                                        com.android.internal.R.color
+                                                .system_notification_accent_color))
+                                .setContentTitle(title)
+                                .setContentText(message)
+                                .setContentIntent(pi)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .extend(new Notification.TvExtender()
+                                        .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
+                                .build();
+                mAdbNotificationShown = true;
+                mNotificationManager.notifyAsUser(null, id, notification,
+                        UserHandle.ALL);
+            } else {
+                mAdbNotificationShown = false;
+                mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
+            }
+        }
+
+        private void startAdbDebuggingThread() {
+            ++mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 1) {
+                return;
+            }
+
+            registerForAuthTimeChanges();
+            mThread = new AdbDebuggingThread();
+            mThread.start();
+
+            mAdbKeyStore.updateKeyStore();
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
+        private void stopAdbDebuggingThread() {
+            --mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 0) {
+                return;
+            }
+
+            if (mThread != null) {
+                mThread.stopListening();
+                mThread = null;
+            }
+
+            if (!mConnectedKeys.isEmpty()) {
+                for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                    mAdbKeyStore.setLastConnectionTime(entry.getKey(),
+                            System.currentTimeMillis());
+                }
+                sendPersistKeyStoreMessage();
+                mConnectedKeys.clear();
+                mWifiConnectedKeys.clear();
+            }
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
         public void handleMessage(Message msg) {
+            if (mAdbKeyStore == null) {
+                mAdbKeyStore = new AdbKeyStore();
+            }
+
             switch (msg.what) {
                 case MESSAGE_ADB_ENABLED:
-                    if (mAdbEnabled) {
+                    if (mAdbUsbEnabled) {
                         break;
                     }
-                    registerForAuthTimeChanges();
-                    mAdbEnabled = true;
-
-                    mThread = new AdbDebuggingThread();
-                    mThread.start();
-
-                    mAdbKeyStore = new AdbKeyStore();
-                    mAdbKeyStore.updateKeyStore();
-                    scheduleJobToUpdateAdbKeyStore();
+                    startAdbDebuggingThread();
+                    mAdbUsbEnabled = true;
                     break;
 
                 case MESSAGE_ADB_DISABLED:
-                    if (!mAdbEnabled) {
+                    if (!mAdbUsbEnabled) {
                         break;
                     }
-
-                    mAdbEnabled = false;
-
-                    if (mThread != null) {
-                        mThread.stopListening();
-                        mThread = null;
-                    }
-
-                    if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
-                                    System.currentTimeMillis());
-                        }
-                        sendPersistKeyStoreMessage();
-                        mConnectedKeys.clear();
-                    }
-                    scheduleJobToUpdateAdbKeyStore();
+                    stopAdbDebuggingThread();
+                    mAdbUsbEnabled = false;
                     break;
 
                 case MESSAGE_ADB_ALLOW: {
@@ -367,8 +877,8 @@
                     if (mThread != null) {
                         mThread.sendResponse("OK");
                         if (alwaysAllow) {
-                            if (!mConnectedKeys.contains(key)) {
-                                mConnectedKeys.add(key);
+                            if (!mConnectedKeys.containsKey(key)) {
+                                mConnectedKeys.put(key, 1);
                             }
                             mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                             sendPersistKeyStoreMessage();
@@ -407,7 +917,7 @@
                     }
                     logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
                     mFingerprints = fingerprints;
-                    startConfirmation(key, mFingerprints);
+                    startConfirmationForKey(key, mFingerprints);
                     break;
                 }
 
@@ -419,6 +929,7 @@
                     if (mAdbKeyStore == null) {
                         mAdbKeyStore = new AdbKeyStore();
                     }
+                    mWifiConnectedKeys.clear();
                     mAdbKeyStore.deleteKeyStore();
                     cancelJobToUpdateAdbKeyStore();
                     break;
@@ -428,12 +939,17 @@
                     String key = (String) msg.obj;
                     boolean alwaysAllow = false;
                     if (key != null && key.length() > 0) {
-                        if (mConnectedKeys.contains(key)) {
+                        if (mConnectedKeys.containsKey(key)) {
                             alwaysAllow = true;
-                            mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
-                            sendPersistKeyStoreMessage();
-                            scheduleJobToUpdateAdbKeyStore();
-                            mConnectedKeys.remove(key);
+                            int refcount = mConnectedKeys.get(key) - 1;
+                            if (refcount == 0) {
+                                mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+                                sendPersistKeyStoreMessage();
+                                scheduleJobToUpdateAdbKeyStore();
+                                mConnectedKeys.remove(key);
+                            } else {
+                                mConnectedKeys.put(key, refcount);
+                            }
                         }
                     } else {
                         Slog.w(TAG, "Received a disconnected key message with an empty key");
@@ -451,8 +967,8 @@
 
                 case MESSAGE_ADB_UPDATE_KEYSTORE: {
                     if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
+                        for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                            mAdbKeyStore.setLastConnectionTime(entry.getKey(),
                                     System.currentTimeMillis());
                         }
                         sendPersistKeyStoreMessage();
@@ -469,8 +985,10 @@
                     if (key == null || key.length() == 0) {
                         Slog.w(TAG, "Received a connected key message with an empty key");
                     } else {
-                        if (!mConnectedKeys.contains(key)) {
-                            mConnectedKeys.add(key);
+                        if (!mConnectedKeys.containsKey(key)) {
+                            mConnectedKeys.put(key, 1);
+                        } else {
+                            mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
                         }
                         mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                         sendPersistKeyStoreMessage();
@@ -479,6 +997,199 @@
                     }
                     break;
                 }
+                case MSG_ADBDWIFI_ENABLE: {
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+
+                    AdbConnectionInfo currentInfo = getCurrentWifiApInfo();
+                    if (currentInfo == null) {
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    if (!verifyWifiNetwork(currentInfo.getBSSID(),
+                            currentInfo.getSSID())) {
+                        // This means that the network is not in the list of trusted networks.
+                        // We'll give user a prompt on whether to allow wireless debugging on
+                        // the current wifi network.
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    setAdbConnectionInfo(currentInfo);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                }
+                case MSG_ADBDWIFI_DISABLE:
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    mAdbWifiEnabled = false;
+                    setAdbConnectionInfo(null);
+                    mContext.unregisterReceiver(mBroadcastReceiver);
+
+                    if (mThread != null) {
+                        mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
+                    }
+                    onAdbdWifiServerDisconnected(-1);
+                    stopAdbDebuggingThread();
+                    break;
+                case MSG_ADBWIFI_ALLOW:
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+                    String bssid = (String) msg.obj;
+                    boolean alwaysAllow = msg.arg1 == 1;
+                    if (alwaysAllow) {
+                        mAdbKeyStore.addTrustedNetwork(bssid);
+                    }
+
+                    // Let's check again to make sure we didn't switch networks while verifying
+                    // the wifi bssid.
+                    AdbConnectionInfo newInfo = getCurrentWifiApInfo();
+                    if (newInfo == null || !bssid.equals(newInfo.getBSSID())) {
+                        break;
+                    }
+
+                    setAdbConnectionInfo(newInfo);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                case MSG_ADBWIFI_DENY:
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    sendServerConnectionState(false, -1);
+                    break;
+                case MSG_REQ_UNPAIR: {
+                    String fingerprint = (String) msg.obj;
+                    // Tell adbd to disconnect the device if connected.
+                    String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint);
+                    if (publicKey == null || publicKey.isEmpty()) {
+                        Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]");
+                        break;
+                    }
+                    String cmdStr = MSG_DISCONNECT_DEVICE + publicKey;
+                    if (mThread != null) {
+                        mThread.sendResponse(cmdStr);
+                    }
+                    mAdbKeyStore.removeKey(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_RESULT: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String publicKey = bundle.getString("publicKey");
+                    onPairingResult(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_PORT: {
+                    int port = (int) msg.obj;
+                    sendPairingPortToUI(port);
+                    break;
+                }
+                case MSG_PAIR_PAIRING_CODE: {
+                    String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
+                    updateUIPairCode(pairingCode);
+                    mPairingThread = new PairingThread(pairingCode, null);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIR_QR_CODE: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String serviceName = bundle.getString("serviceName");
+                    String password = bundle.getString("password");
+                    mPairingThread = new PairingThread(password, serviceName);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIRING_CANCEL:
+                    if (mPairingThread != null) {
+                        mPairingThread.cancelPairing();
+                        try {
+                            mPairingThread.join();
+                        } catch (InterruptedException e) {
+                            Slog.w(TAG, "Error while waiting for pairing thread to quit.");
+                            e.printStackTrace();
+                        }
+                        mPairingThread = null;
+                    }
+                    break;
+                case MSG_WIFI_DEVICE_CONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.add(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        showAdbConnectedNotification(true);
+                    }
+                    break;
+                }
+                case MSG_WIFI_DEVICE_DISCONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.remove(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        if (mWifiConnectedKeys.isEmpty()) {
+                            showAdbConnectedNotification(false);
+                        }
+                    }
+                    break;
+                }
+                case MSG_SERVER_CONNECTED: {
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerConnected(port);
+                    synchronized (mAdbConnectionInfo) {
+                        mAdbConnectionInfo.setPort(port);
+                    }
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    break;
+                }
+                case MSG_SERVER_DISCONNECTED: {
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerDisconnected(port);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    stopAdbDebuggingThread();
+                    if (mConnectionPortPoller != null) {
+                        mConnectionPortPoller.cancelAndWait();
+                        mConnectionPortPoller = null;
+                    }
+                    break;
+                }
             }
         }
 
@@ -540,6 +1251,142 @@
         private void cancelJobToUpdateAdbKeyStore() {
             removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE);
         }
+
+        // Generates a random string of digits with size |size|.
+        private String createPairingCode(int size) {
+            String res = "";
+            SecureRandom rand = new SecureRandom();
+            for (int i = 0; i < size; ++i) {
+                res += rand.nextInt(10);
+            }
+
+            return res;
+        }
+
+        private void sendServerConnectionState(boolean connected, int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected
+                    ? AdbManager.WIRELESS_STATUS_CONNECTED
+                    : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void onAdbdWifiServerConnected(int port) {
+            // Send the paired devices list to the UI
+            sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+            sendServerConnectionState(true, port);
+        }
+
+        private void onAdbdWifiServerDisconnected(int port) {
+            // The TLS server disconnected while we had wireless debugging enabled.
+            // Let's disable it.
+            mWifiConnectedKeys.clear();
+            showAdbConnectedNotification(false);
+            sendServerConnectionState(false, port);
+        }
+
+        /**
+         * Returns the [bssid, ssid] of the current access point.
+         */
+        private AdbConnectionInfo getCurrentWifiApInfo() {
+            WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+            if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi.");
+                return null;
+            }
+
+            String ssid = null;
+            if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
+                ssid = wifiInfo.getPasspointProviderFriendlyName();
+            } else {
+                ssid = wifiInfo.getSSID();
+                if (ssid == null || WifiManager.UNKNOWN_SSID.equals(ssid)) {
+                    // OK, it's not in the connectionInfo; we have to go hunting for it
+                    List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
+                    int length = networks.size();
+                    for (int i = 0; i < length; i++) {
+                        if (networks.get(i).networkId == wifiInfo.getNetworkId()) {
+                            ssid = networks.get(i).SSID;
+                        }
+                    }
+                    if (ssid == null) {
+                        Slog.e(TAG, "Unable to get ssid of the wifi AP.");
+                        return null;
+                    }
+                }
+            }
+
+            String bssid = wifiInfo.getBSSID();
+            if (bssid == null || bssid.isEmpty()) {
+                Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
+                return null;
+            }
+            return new AdbConnectionInfo(bssid, ssid);
+        }
+
+        private boolean verifyWifiNetwork(String bssid, String ssid) {
+            // Check against a list of user-trusted networks.
+            if (mAdbKeyStore.isTrustedNetwork(bssid)) {
+                return true;
+            }
+
+            // Ask user to confirm using wireless debugging on this network.
+            startConfirmationForNetwork(ssid, bssid);
+            return false;
+        }
+
+        private void onPairingResult(String publicKey) {
+            if (publicKey == null) {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_SUCCESS);
+                String fingerprints = getFingerprints(publicKey);
+                String hostname = "nouser@nohostname";
+                String[] args = publicKey.split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                PairDevice device = new PairDevice(fingerprints, hostname, false);
+                intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                // Add the key into the keystore
+                mAdbKeyStore.setLastConnectionTime(publicKey,
+                        System.currentTimeMillis());
+                sendPersistKeyStoreMessage();
+                scheduleJobToUpdateAdbKeyStore();
+            }
+        }
+
+        private void sendPairingPortToUI(int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_CONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+            // Map is not serializable, so need to downcast
+            intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void updateUIPairCode(String code) {
+            if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code);
+
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_PAIRING_CODE);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     private String getFingerprints(String key) {
@@ -576,7 +1423,34 @@
         return sb.toString();
     }
 
-    private void startConfirmation(String key, String fingerprints) {
+    private void startConfirmationForNetwork(String ssid, String bssid) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
+        int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
+        String componentString;
+        if (userInfo.isAdmin()) {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        } else {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        }
+        ComponentName componentName = ComponentName.unflattenFromString(componentString);
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
+                || startConfirmationService(componentName, userInfo.getUserHandle(),
+                        extras)) {
+            return;
+        }
+        Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
+                + componentString + " as an Activity or a Service");
+    }
+
+    private void startConfirmationForKey(String key, String fingerprints) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("key", key));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints));
         int currentUserId = ActivityManager.getCurrentUser();
         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
         String componentString;
@@ -591,9 +1465,9 @@
                     R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
         }
         ComponentName componentName = ComponentName.unflattenFromString(componentString);
-        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
                 || startConfirmationService(componentName, userInfo.getUserHandle(),
-                        key, fingerprints)) {
+                        extras)) {
             return;
         }
         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
@@ -604,9 +1478,9 @@
      * @return true if the componentName led to an Activity that was started.
      */
     private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
+            List<Map.Entry<String, String>> extras) {
         PackageManager packageManager = mContext.getPackageManager();
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+        Intent intent = createConfirmationIntent(componentName, extras);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
             try {
@@ -623,8 +1497,8 @@
      * @return true if the componentName led to a Service that was started.
      */
     private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+            List<Map.Entry<String, String>> extras) {
+        Intent intent = createConfirmationIntent(componentName, extras);
         try {
             if (mContext.startServiceAsUser(intent, userHandle) != null) {
                 return true;
@@ -635,12 +1509,13 @@
         return false;
     }
 
-    private Intent createConfirmationIntent(ComponentName componentName, String key,
-            String fingerprints) {
+    private Intent createConfirmationIntent(ComponentName componentName,
+            List<Map.Entry<String, String>> extras) {
         Intent intent = new Intent();
         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
-        intent.putExtra("key", key);
-        intent.putExtra("fingerprints", fingerprints);
+        for (Map.Entry<String, String> entry : extras) {
+            intent.putExtra(entry.getKey(), entry.getValue());
+        }
         return intent;
     }
 
@@ -733,7 +1608,8 @@
             mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
                                               : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
         } else if (transportType == AdbTransportType.WIFI) {
-            // TODO(joshuaduong): Not implemented
+            mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
+                                              : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
         } else {
             throw new IllegalArgumentException(
                     "setAdbEnabled called with unimplemented transport type=" + transportType);
@@ -767,6 +1643,87 @@
     }
 
     /**
+     * Allows wireless debugging on the network identified by {@code bssid} either once
+     * or always if {@code alwaysAllow} is {@code true}.
+     */
+    public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+        Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW);
+        msg.arg1 = alwaysAllow ? 1 : 0;
+        msg.obj = bssid;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Denies wireless debugging connection on the last requested network.
+     */
+    public void denyWirelessDebugging() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY);
+    }
+
+    /**
+     * Returns the port adbwifi is currently opened on.
+     */
+    public int getAdbWirelessPort() {
+        AdbConnectionInfo info = getAdbConnectionInfo();
+        if (info == null) {
+            return 0;
+        }
+        return info.getPort();
+    }
+
+    /**
+     * Returns the list of paired devices.
+     */
+    public Map<String, PairDevice> getPairedDevices() {
+        AdbKeyStore keystore = new AdbKeyStore();
+        return keystore.getPairedDevices();
+    }
+
+    /**
+     * Unpair with device
+     */
+    public void unpairDevice(String fingerprint) {
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_REQ_UNPAIR,
+                                         fingerprint);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByPairingCode() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByQrCode(String serviceName, String password) {
+        Bundle bundle = new Bundle();
+        bundle.putString("serviceName", serviceName);
+        bundle.putString("password", password);
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_PAIR_QR_CODE,
+                                         bundle);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Disables pairing
+     */
+    public void disablePairing() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL);
+    }
+
+    /**
+     * Status enabled/disabled check
+     */
+    public boolean isAdbWifiEnabled() {
+        return mAdbWifiEnabled;
+    }
+
+    /**
      * Sends a message to the handler to persist the keystore.
      */
     private void sendPersistKeyStoreMessage() {
@@ -819,9 +1776,19 @@
         private File mKeyFile;
         private AtomicFile mAtomicKeyFile;
 
+        private List<String> mTrustedNetworks;
+        private static final int KEYSTORE_VERSION = 1;
+        private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
+        private static final String XML_KEYSTORE_START_TAG = "keyStore";
+        private static final String XML_ATTRIBUTE_VERSION = "version";
         private static final String XML_TAG_ADB_KEY = "adbKey";
         private static final String XML_ATTRIBUTE_KEY = "key";
         private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
+        // A list of trusted networks a device can always wirelessly debug on (always allow).
+        // TODO: Move trusted networks list into a different file?
+        private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP";
+        private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid";
+
         private static final String SYSTEM_KEY_FILE = "/adb_keys";
 
         /**
@@ -848,10 +1815,48 @@
         private void init() {
             initKeyFile();
             mKeyMap = getKeyMap();
+            mTrustedNetworks = getTrustedNetworks();
             mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
             addUserKeysToKeyStore();
         }
 
+        public void addTrustedNetwork(String bssid) {
+            mTrustedNetworks.add(bssid);
+            sendPersistKeyStoreMessage();
+        }
+
+        public Map<String, PairDevice> getPairedDevices() {
+            Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>();
+            for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
+                String fingerprints = getFingerprints(keyEntry.getKey());
+                String hostname = "nouser@nohostname";
+                String[] args = keyEntry.getKey().split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                pairedDevices.put(keyEntry.getKey(), new PairDevice(
+                        hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+            }
+            return pairedDevices;
+        }
+
+        public String findKeyFromFingerprint(String fingerprint) {
+            for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) {
+                String f = getFingerprints(entry.getKey());
+                if (fingerprint.equals(f)) {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+
+        public void removeKey(String key) {
+            if (mKeyMap.containsKey(key)) {
+                mKeyMap.remove(key);
+                sendPersistKeyStoreMessage();
+            }
+        }
+
         /**
          * Initializes the key file that will be used to persist the adb grants.
          */
@@ -921,6 +1926,78 @@
             try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return keyMap;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION + ")");
+                        return keyMap;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+                    long connectionTime;
+                    try {
+                        connectionTime = Long.valueOf(
+                                parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG,
+                                "Caught a NumberFormatException parsing the last connection time: "
+                                        + e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    keyMap.put(key, connectionTime);
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
+            } catch (XmlPullParserException e) {
+                Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
+                // The file could be written in a format prior to introducing keystore tag.
+                return getKeyMapBeforeKeystoreVersion();
+            }
+            return keyMap;
+        }
+
+
+        /**
+         * Returns the key map with the keys and last connection times from the key file.
+         * This implementation was prior to adding the XML_KEYSTORE_START_TAG.
+         */
+        private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
+            Map<String, Long> keyMap = new HashMap<String, Long>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return keyMap;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return keyMap;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
                 XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
                 while (parser.next() != XmlPullParser.END_DOCUMENT) {
                     String tagName = parser.getName();
@@ -951,6 +2028,63 @@
         }
 
         /**
+         * Returns the map of trusted networks from the keystore file.
+         *
+         * This was implemented in keystore version 1.
+         */
+        private List<String> getTrustedNetworks() {
+            List<String> trustedNetworks = new ArrayList<String>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return trustedNetworks;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return trustedNetworks;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return trustedNetworks;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION);
+                        return trustedNetworks;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
+                    trustedNetworks.add(bssid);
+                }
+            } catch (IOException | XmlPullParserException | NumberFormatException e) {
+                Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
+            }
+            return trustedNetworks;
+        }
+
+        /**
          * Updates the keystore with keys that were previously set to be always allowed before the
          * connection time of keys was tracked.
          */
@@ -986,7 +2120,7 @@
             // if there is nothing in the key map then ensure any keys left in the keystore files
             // are deleted as well.
             filterOutOldKeys();
-            if (mKeyMap.isEmpty()) {
+            if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) {
                 deleteKeyStore();
                 return;
             }
@@ -1004,6 +2138,8 @@
                 serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
                 serializer.startDocument(null, true);
 
+                serializer.startTag(null, XML_KEYSTORE_START_TAG);
+                serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
                     serializer.startTag(null, XML_TAG_ADB_KEY);
                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
@@ -1011,7 +2147,12 @@
                             String.valueOf(keyEntry.getValue()));
                     serializer.endTag(null, XML_TAG_ADB_KEY);
                 }
-
+                for (String bssid : mTrustedNetworks) {
+                    serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                    serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid);
+                    serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                }
+                serializer.endTag(null, XML_KEYSTORE_START_TAG);
                 serializer.endDocument();
                 mAtomicKeyFile.finishWrite(keyStream);
             } catch (IOException e) {
@@ -1072,6 +2213,7 @@
          */
         public void deleteKeyStore() {
             mKeyMap.clear();
+            mTrustedNetworks.clear();
             deleteKeyFile();
             if (mAtomicKeyFile == null) {
                 return;
@@ -1153,5 +2295,14 @@
                 return false;
             }
         }
+
+        /**
+         * Returns whether the specified bssid is in the list of trusted networks. This requires
+         * that the user previously allowed wireless debugging on this network and selected the
+         * option to 'Always allow'.
+         */
+        public boolean isTrustedNetwork(String bssid) {
+            return mTrustedNetworks.contains(bssid);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 0d161b9..7ccb284 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -21,8 +21,10 @@
 import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbManagerInternal;
 import android.debug.AdbTransportType;
 import android.debug.IAdbManager;
@@ -34,6 +36,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.adb.AdbServiceDumpProto;
 import android.sysprop.AdbProperties;
@@ -44,6 +47,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -54,6 +58,7 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -61,6 +66,27 @@
  */
 public class AdbService extends IAdbManager.Stub {
     /**
+     * Adb native daemon.
+     */
+    static final String ADBD = "adbd";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_START = "ctl.start";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_STOP = "ctl.stop";
+
+    // The tcp port adb is currently using
+    AtomicInteger mConnectionPort = new AtomicInteger(-1);
+
+    private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
+    private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+
+    /**
      * Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
      */
     public static class Lifecycle extends SystemService {
@@ -129,9 +155,8 @@
             mIsAdbUsbEnabled = containsFunction(
                     SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
                     UsbManager.USB_FUNCTION_ADB);
-            // TODO(joshuaduong): Read the adb wifi state from a persistent system
-            // property (persist.sys.adb.wifi).
-            mIsAdbWifiEnabled = false;
+            mIsAdbWifiEnabled = "1".equals(
+                    SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
 
             // register observer to listen for settings changes
             mObserver = new AdbSettingsObserver();
@@ -189,6 +214,7 @@
      * May also contain vendor-specific default functions for testing purposes.
      */
     private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -245,8 +271,9 @@
     }
 
     @Override
-    public void allowDebugging(boolean alwaysAllow, String publicKey) {
+    public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(publicKey);
         if (mDebuggingManager != null) {
             mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
         }
@@ -296,53 +323,118 @@
     }
 
     @Override
-    public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+    public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(bssid);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
+        }
     }
 
     @Override
     public void denyWirelessDebugging() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.denyWirelessDebugging();
+        }
     }
 
     @Override
     public Map<String, PairDevice> getPairedDevices() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getPairedDevices();
+        }
         return null;
     }
 
     @Override
-    public void unpairDevice(String fingerprint) {
+    public void unpairDevice(@NonNull String fingerprint) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(fingerprint);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.unpairDevice(fingerprint);
+        }
     }
 
     @Override
     public void enablePairingByPairingCode() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByPairingCode();
+        }
     }
 
     @Override
-    public void enablePairingByQrCode(String serviceName, String password) {
+    public void enablePairingByQrCode(@NonNull String serviceName, @NonNull String password) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkStringNotEmpty(password);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByQrCode(serviceName, password);
+        }
     }
 
     @Override
     public void disablePairing() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.disablePairing();
+        }
     }
 
     @Override
     public int getAdbWirelessPort() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
-        return 0;
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getAdbWirelessPort();
+        }
+        // If ro.adb.secure=0
+        return mConnectionPort.get();
+    }
+
+    /**
+     * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
+     * do this.
+     */
+    class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            if (port > 0 && port <= 65535) {
+                mConnectionPort.set(port);
+            } else {
+                mConnectionPort.set(-1);
+                // Turn off wifi debugging, since the server did not start.
+                try {
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                } catch (SecurityException e) {
+                    // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+                    // be changed.
+                    Slog.d(TAG, "ADB_ENABLED is restricted.");
+                }
+            }
+            broadcastPortInfo(mConnectionPort.get());
+        }
+    }
+
+    private void broadcastPortInfo(int port) {
+        Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
+                ? AdbManager.WIRELESS_STATUS_CONNECTED
+                : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+        intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        Slog.i(TAG, "sent port broadcast port=" + port);
+    }
+
+    private void startAdbd() {
+        SystemProperties.set(CTL_START, ADBD);
+    }
+
+    private void stopAdbd() {
+        if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) {
+            SystemProperties.set(CTL_STOP, ADBD);
+        }
     }
 
     private void setAdbEnabled(boolean enable, byte transportType) {
@@ -356,11 +448,33 @@
             mIsAdbUsbEnabled = enable;
         } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
             mIsAdbWifiEnabled = enable;
+            if (mIsAdbWifiEnabled) {
+                if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
+                    // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+                }
+            } else {
+                // Stop adb over WiFi.
+                SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
+                if (mConnectionPortPoller != null) {
+                    mConnectionPortPoller.cancelAndWait();
+                    mConnectionPortPoller = null;
+                }
+            }
         } else {
             // No change
             return;
         }
 
+        if (enable) {
+            startAdbd();
+        } else {
+            stopAdbd();
+        }
+
         for (IAdbTransport transport : mTransports.values()) {
             try {
                 transport.onAdbEnabled(enable, transportType);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fb48db4..a7125b4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -146,6 +146,7 @@
     static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
     static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
     static final int USER_UNLOCK_MSG = 100;
+    static final int USER_UNLOCKED_MSG = 105;
     static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
     static final int START_USER_SWITCH_FG_MSG = 120;
 
@@ -625,6 +626,9 @@
                     FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED,
                     elapsedTimeMs);
         }
+
+        mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget();
+
         final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
         bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -2366,6 +2370,9 @@
                 });
                 finishUserUnlocked((UserState) msg.obj);
                 break;
+            case USER_UNLOCKED_MSG:
+                mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
+                break;
             case USER_CURRENT_MSG:
                 mInjector.batteryStatsServiceNoteEvent(
                         BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 8206fef..2672f84 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -713,7 +713,7 @@
 
     /**
      * Gets a list of all supported users (i.e., those that pass the
-     * {@link #isSupportedUser(TargetUser)}check).
+     * {@link #isUserSupported(TargetUser)}check).
      */
     @NonNull
     protected List<UserInfo> getSupportedUsers() {
@@ -722,7 +722,7 @@
         final List<UserInfo> supportedUsers = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {
             final UserInfo userInfo = allUsers[i];
-            if (isSupportedUser(new TargetUser(userInfo))) {
+            if (isUserSupported(new TargetUser(userInfo))) {
                 supportedUsers.add(userInfo);
             }
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 87262a8..6f04af6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1882,7 +1882,7 @@
                 executeOrSendMessage(mCurMethod,
                         mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
                                 requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
-                                        imi.getPackageName())));
+                                        imi.getPackageName(), mCurTokenDisplayId)));
             } else {
                 callback.onInlineSuggestionsUnsupported();
             }
@@ -1902,11 +1902,14 @@
         @NonNull
         private final String mImePackageName;
 
+        private final int mImeDisplayId;
+
         InlineSuggestionsRequestCallbackDecorator(
                 @NonNull IInlineSuggestionsRequestCallback callback,
-                @NonNull String imePackageName) {
+                @NonNull String imePackageName, int displayId) {
             mCallback = callback;
             mImePackageName = imePackageName;
+            mImeDisplayId = displayId;
         }
 
         @Override
@@ -1923,6 +1926,7 @@
                                 + "] doesn't match the IME package name=[" + mImePackageName
                                 + "].");
             }
+            request.setHostDisplayId(mImeDisplayId);
             mCallback.onInlineSuggestionsRequest(request, callback);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 077fc6f..2d88080 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10969,10 +10969,10 @@
             // to null here, only to reset them at a later point.
             Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
                     destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
-                    AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
-                    AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
-                    PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
-                    PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
+                    AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+                    AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+                    PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+                    PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
                     UserManagerService.getInstance(),
                     usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
                     parsedPackage.getMimeGroups());
@@ -11164,6 +11164,8 @@
         // TODO(b/135203078): Remove, move to constructor
         pkgSetting.pkg = parsedPackage;
         pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
+        pkgSetting.pkgPrivateFlags =
+                PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
         if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
             pkgSetting.versionCode = parsedPackage.getLongVersionCode();
         }
@@ -16954,6 +16956,7 @@
                     final boolean vendor = oldPackage.isVendor();
                     final boolean product = oldPackage.isProduct();
                     final boolean odm = oldPackage.isOdm();
+                    final boolean systemExt = oldPackage.isSystemExt();
                     final @ParseFlags int systemParseFlags = parseFlags;
                     final @ScanFlags int systemScanFlags = scanFlags
                             | SCAN_AS_SYSTEM
@@ -16961,14 +16964,14 @@
                             | (oem ? SCAN_AS_OEM : 0)
                             | (vendor ? SCAN_AS_VENDOR : 0)
                             | (product ? SCAN_AS_PRODUCT : 0)
-                            | (odm ? SCAN_AS_ODM : 0);
+                            | (odm ? SCAN_AS_ODM : 0)
+                            | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
 
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
                                 + ", old=" + oldPackage);
                     }
                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-                    ps.getPkgState().setUpdatedSystemApp(true);
                     targetParseFlags = systemParseFlags;
                     targetScanFlags = systemScanFlags;
                 } else { // non system replace
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index af5c536..2453318 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -521,6 +521,9 @@
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups);
+        if (ret != null) {
+            ret.getPkgState().setUpdatedSystemApp(false);
+        }
         mDisabledSysPackages.remove(name);
         return ret;
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 840c865d..f647b6a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2794,7 +2794,7 @@
             }
 
             if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
-                    !ps.isSystem() || !ps.getPkgState().isUpdatedSystemApp()) {
+                    !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
                 // This is the first that we have heard about this package, so the
                 // permissions we have now selected are fixed until explicitly
                 // changed.
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index c008d93..e27bf48 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -35,6 +35,9 @@
  *
  * It is assumed that anything inside the package was not cached or written to disk, so none of
  * these fields are either. They must be set on every boot from other state on the device.
+ *
+ * These fields are also not copied into any cloned PackageSetting, to preserve the old behavior
+ * where they would be lost implicitly by re-generating the package object.
  */
 @DataClass(genSetters = true, genConstructor = false, genBuilder = false)
 public class PackageStateUnserialized {
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8d090f1..801d75b 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -218,11 +218,7 @@
                         packageInstaller.getSessionInfo(sessionId);
                 if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
                     mContext.unregisterReceiver(listener);
-                    if (logPackage != null) {
-                        // We save the rollback id so that after reboot, we can log if rollback was
-                        // successful or not. If logPackage is null, then there is nothing to log.
-                        saveStagedRollbackId(rollbackId);
-                    }
+                    saveStagedRollbackId(rollbackId);
                     WatchdogRollbackLogger.logEvent(logPackage,
                             FrameworkStatsLog
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 1be6f22..659de00 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -159,6 +159,12 @@
             return;
         }
 
+        // If no logging packages are found, use a null package to ensure the rollback status
+        // is still logged.
+        if (oldLoggingPackages.isEmpty()) {
+            oldLoggingPackages.add(null);
+        }
+
         for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
             if (sessionInfo.isStagedSessionApplied()) {
                 logEvent(oldLoggingPackage,
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index aed2d9b..3c8ef6c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -395,6 +395,8 @@
                     case FrameworkStatsLog.BATTERY_VOLTAGE:
                     case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
                         return pullHealthHal(atomTag, data);
+                    case FrameworkStatsLog.APP_FEATURES_OPS:
+                        return pullAppFeaturesOps(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -550,6 +552,7 @@
         registerAppsOnExternalStorageInfo();
         registerFaceSettings();
         registerAppOps();
+        registerAppFeaturesOps();
         registerRuntimeAppOpAccessMessage();
         registerNotificationRemoteViews();
         registerDangerousPermissionState();
@@ -2843,7 +2846,6 @@
                 BackgroundThread.getExecutor(),
                 mStatsCallbackImpl
         );
-
     }
 
     private void registerRuntimeAppOpAccessMessage() {
@@ -2854,7 +2856,6 @@
                 BackgroundThread.getExecutor(),
                 mStatsCallbackImpl
         );
-
     }
 
     int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
@@ -2917,6 +2918,84 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerAppFeaturesOps() {
+        int tagId = FrameworkStatsLog.APP_FEATURES_OPS;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+            CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+            HistoricalOpsRequest histOpsRequest =
+                    new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags(
+                            OP_FLAGS_PULLED).build();
+            appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+
+            HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+                    TimeUnit.MILLISECONDS);
+
+            for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+                final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+                final int uid = uidOps.getUid();
+                for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+                    final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+                    for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount();
+                            featureIdx++) {
+                        final AppOpsManager.HistoricalFeatureOps featureOps =
+                                packageOps.getFeatureOpsAt(featureIdx);
+                        for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) {
+                            final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx);
+                            StatsEvent.Builder e = StatsEvent.newBuilder();
+                            e.setAtomId(atomTag);
+                            e.writeInt(uid);
+                            e.writeString(packageOps.getPackageName());
+                            e.writeString(featureOps.getFeatureId());
+                            e.writeString(op.getOpName());
+                            e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+
+                            String perm = AppOpsManager.opToPermission(op.getOpCode());
+                            if (perm == null) {
+                                e.writeBoolean(false);
+                            } else {
+                                PermissionInfo permInfo;
+                                try {
+                                    permInfo = mContext.getPackageManager().getPermissionInfo(perm,
+                                            0);
+                                    e.writeBoolean(
+                                            permInfo.getProtection() == PROTECTION_DANGEROUS);
+                                } catch (PackageManager.NameNotFoundException exception) {
+                                    e.writeBoolean(false);
+                                }
+                            }
+                            pulledData.add(e.build());
+                        }
+
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            // TODO: catch exceptions at a more granular level
+            Slog.e(TAG, "Could not read appops", t);
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) {
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9cb5ba7..e0f8f0e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8075,32 +8075,39 @@
     public void getWindowInsets(WindowManager.LayoutParams attrs,
             int displayId, Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper displayCutout) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
-            if (dc == null) {
-                throw new WindowManager.InvalidDisplayException("Display#" + displayId
-                        + "could not be found!");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = getDisplayContentOrCreate(displayId, attrs.token);
+                if (dc == null) {
+                    throw new WindowManager.InvalidDisplayException("Display#" + displayId
+                            + "could not be found!");
+                }
+                final WindowToken windowToken = dc.getWindowToken(attrs.token);
+                final ActivityRecord activity;
+                if (windowToken != null && windowToken.asActivityRecord() != null) {
+                    activity = windowToken.asActivityRecord();
+                } else {
+                    activity = null;
+                }
+                final Rect taskBounds;
+                final boolean floatingStack;
+                if (activity != null && activity.getTask() != null) {
+                    final Task task = activity.getTask();
+                    taskBounds = new Rect();
+                    task.getBounds(taskBounds);
+                    floatingStack = task.isFloating();
+                } else {
+                    taskBounds = null;
+                    floatingStack = false;
+                }
+                final DisplayFrames displayFrames = dc.mDisplayFrames;
+                final DisplayPolicy policy = dc.getDisplayPolicy();
+                policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack,
+                        new Rect(), outContentInsets, outStableInsets, displayCutout);
             }
-            final WindowToken windowToken = dc.getWindowToken(attrs.token);
-            final ActivityRecord activity;
-            if (windowToken != null && windowToken.asActivityRecord() != null) {
-                activity = windowToken.asActivityRecord();
-            } else {
-                activity = null;
-            }
-            final Rect taskBounds = new Rect();
-            final boolean floatingStack;
-            if (activity != null && activity.getTask() != null) {
-                final Task task = activity.getTask();
-                task.getBounds(taskBounds);
-                floatingStack = task.isFloating();
-            } else {
-                floatingStack = false;
-            }
-            final DisplayFrames displayFrames = dc.mDisplayFrames;
-            final DisplayPolicy policy = dc.getDisplayPolicy();
-            policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack,
-                    new Rect(), outContentInsets, outStableInsets, displayCutout);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index e888f2a..27bd58e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -24,6 +24,7 @@
         "BroadcastRadio/regions.cpp",
         "stats/PowerStatsPuller.cpp",
         "stats/SubsystemSleepStatePuller.cpp",
+        "com_android_server_adb_AdbDebuggingManager.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_connectivity_Vpn.cpp",
         "com_android_server_ConsumerIrService.cpp",
@@ -86,6 +87,8 @@
 cc_defaults {
     name: "libservices.core-libs",
     shared_libs: [
+        "libadb_pairing_server",
+        "libadb_pairing_connection",
         "libandroid_runtime",
         "libandroidfw",
         "libaudioclient",
diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
new file mode 100644
index 0000000..9c834aa
--- /dev/null
+++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 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 "AdbDebuggingManager-JNI"
+
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <random>
+#include <string>
+#include <vector>
+
+#include <adb/pairing/pairing_server.h>
+#include <android-base/properties.h>
+#include <utils/Log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+namespace {
+
+template <class T, class N>
+class JSmartWrapper {
+public:
+    JSmartWrapper(JNIEnv* env, T* jData) : mEnv(env), mJData(jData) {}
+
+    virtual ~JSmartWrapper() = default;
+
+    const N* data() const { return mRawData; }
+
+    jsize size() const { return mSize; }
+
+protected:
+    N* mRawData = nullptr;
+    JNIEnv* mEnv = nullptr;
+    T* mJData = nullptr;
+    jsize mSize = 0;
+}; // JSmartWrapper
+
+class JStringUTFWrapper : public JSmartWrapper<jstring, const char> {
+public:
+    explicit JStringUTFWrapper(JNIEnv* env, jstring* str) : JSmartWrapper(env, str) {
+        mRawData = env->GetStringUTFChars(*str, NULL);
+        mSize = env->GetStringUTFLength(*str);
+    }
+
+    virtual ~JStringUTFWrapper() {
+        if (data()) {
+            mEnv->ReleaseStringUTFChars(*mJData, mRawData);
+        }
+    }
+}; // JStringUTFWrapper
+
+struct ServerDeleter {
+    void operator()(PairingServerCtx* p) { pairing_server_destroy(p); }
+};
+using PairingServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
+struct PairingResultWaiter {
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::optional<bool> is_valid_;
+    PeerInfo peer_info_;
+
+    static void ResultCallback(const PeerInfo* peer_info, void* opaque) {
+        auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
+        {
+            std::unique_lock<std::mutex> lock(p->mutex_);
+            if (peer_info) {
+                memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+            }
+            p->is_valid_ = (peer_info != nullptr);
+        }
+        p->cv_.notify_one();
+    }
+};
+
+PairingServerPtr sServer;
+std::unique_ptr<PairingResultWaiter> sWaiter;
+} // namespace
+
+static jint native_pairing_start(JNIEnv* env, jobject thiz, jstring guid, jstring password) {
+    // Server-side only sends its GUID on success.
+    PeerInfo system_info = {};
+    system_info.type = ADB_DEVICE_GUID;
+    JStringUTFWrapper guidWrapper(env, &guid);
+    memcpy(system_info.data, guidWrapper.data(), guidWrapper.size());
+
+    JStringUTFWrapper passwordWrapper(env, &password);
+
+    // Create the pairing server
+    sServer = PairingServerPtr(
+            pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()),
+                                       passwordWrapper.size(), &system_info, 0));
+
+    sWaiter.reset(new PairingResultWaiter);
+    uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get());
+    if (port == 0) {
+        ALOGE("Failed to start pairing server");
+        return -1;
+    }
+
+    return port;
+}
+
+static void native_pairing_cancel(JNIEnv* /* env */, jclass /* clazz */) {
+    if (sServer != nullptr) {
+        sServer.reset();
+    }
+}
+
+static jboolean native_pairing_wait(JNIEnv* env, jobject thiz) {
+    ALOGI("Waiting for pairing server to complete");
+    std::unique_lock<std::mutex> lock(sWaiter->mutex_);
+    if (!sWaiter->is_valid_.has_value()) {
+        sWaiter->cv_.wait(lock, [&]() { return sWaiter->is_valid_.has_value(); });
+    }
+    if (!*(sWaiter->is_valid_)) {
+        return JNI_FALSE;
+    }
+
+    std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
+    // Write to PairingThread's member variables
+    jclass clazz = env->GetObjectClass(thiz);
+    jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
+    jstring jpublickey = env->NewStringUTF(peer_public_key.c_str());
+    env->SetObjectField(thiz, mPublicKey, jpublickey);
+    return JNI_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gPairingThreadMethods[] = {
+        /* name, signature, funcPtr */
+        {"native_pairing_start", "(Ljava/lang/String;Ljava/lang/String;)I",
+         (void*)native_pairing_start},
+        {"native_pairing_cancel", "()V", (void*)native_pairing_cancel},
+        {"native_pairing_wait", "()Z", (void*)native_pairing_wait},
+};
+
+int register_android_server_AdbDebuggingManager(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env,
+                                       "com/android/server/adb/AdbDebuggingManager$PairingThread",
+                                       gPairingThreadMethods, NELEM(gPairingThreadMethods));
+    (void)res; // Faked use when LOG_NDEBUG.
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index f9238e3..a5339a5 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -61,6 +61,7 @@
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
 int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
+int register_android_server_AdbDebuggingManager(JNIEnv* env);
 };
 
 using namespace android;
@@ -115,5 +116,6 @@
     register_android_server_incremental_IncrementalManagerService(env);
     register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
     register_android_server_stats_pull_StatsPullAtomService(env);
+    register_android_server_AdbDebuggingManager(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2499614..5d7f4e9 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -71,12 +71,12 @@
     }
 
     @Override
-    public void onUnlockUser(@NonNull TargetUser targetUser) {
+    public void onUserUnlocking(@NonNull TargetUser targetUser) {
         mDataManager.onUserUnlocked(targetUser.getUserIdentifier());
     }
 
     @Override
-    public void onStopUser(@NonNull TargetUser targetUser) {
+    public void onUserStopping(@NonNull TargetUser targetUser) {
         mDataManager.onUserStopped(targetUser.getUserIdentifier());
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 84f411f..07cc2d4 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1720,21 +1720,6 @@
         private static final int ENUMERATION_TIME_OUT_MS = 2000;
 
         /**
-         * Command to start native service.
-         */
-        protected static final String CTL_START = "ctl.start";
-
-        /**
-         * Command to start native service.
-         */
-        protected static final String CTL_STOP = "ctl.stop";
-
-        /**
-         * Adb native daemon.
-         */
-        protected static final String ADBD = "adbd";
-
-        /**
          * Gadget HAL fully qualified instance name for registering for ServiceNotification.
          */
         protected static final String GADGET_HAL_FQ_NAME =
@@ -1932,17 +1917,7 @@
                     return;
                 }
                 try {
-                    if ((config & UsbManager.FUNCTION_ADB) != 0) {
-                        /**
-                         * Start adbd if ADB function is included in the configuration.
-                         */
-                        setSystemProperty(CTL_START, ADBD);
-                    } else {
-                        /**
-                         * Stop adbd otherwise.
-                         */
-                        setSystemProperty(CTL_STOP, ADBD);
-                    }
+                    // Adbd will be started by AdbService once Global.ADB_ENABLED is set.
                     UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
                             config, chargingFunctions);
                     mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 61f2c50..14c7f04 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -110,18 +110,18 @@
         }
 
         @Override
-        public void onSwitchUser(TargetUser from, TargetUser to) {
+        public void onUserSwitching(TargetUser from, TargetUser to) {
             FgThread.getHandler()
                     .postAtFrontOfQueue(() -> mUsbService.onSwitchUser(to.getUserIdentifier()));
         }
 
         @Override
-        public void onStopUser(TargetUser userInfo) {
+        public void onUserStopping(TargetUser userInfo) {
             mUsbService.onStopUser(userInfo.getUserHandle());
         }
 
         @Override
-        public void onUnlockUser(TargetUser userInfo) {
+        public void onUserUnlocking(TargetUser userInfo) {
             mUsbService.onUnlockUser(userInfo.getUserIdentifier());
         }
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8378d8e..3c0e0af 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return isSupported(user.getUserInfo());
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4604cd2..5f33a3d 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3358,7 +3358,6 @@
         private boolean mImmutable = false;
         public FailureSignalingConnection(DisconnectCause disconnectCause) {
             setDisconnected(disconnectCause);
-            mImmutable = true;
         }
 
         public void checkImmutable() {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5d7d649..7f4fcc0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -330,6 +330,14 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
+     * time the call was created.
+     * @hide
+     */
+    public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS =
+            "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS";
+
+    /**
      * Optional extra for incoming and outgoing calls containing a long which specifies the time
      * telecom began routing the call. This value is in milliseconds since boot.
      * @hide
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a7e52ea..9d77623 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3549,6 +3549,30 @@
     public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
             "show_forwarded_number_bool";
 
+    /**
+     * The list of originating address of missed incoming call SMS. If the SMS has originator
+     * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+     * user.
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+            "missed_incoming_call_sms_originator_string_array";
+
+    /**
+     * The patterns of missed incoming call sms. This is the regular expression used for
+     * matching the missed incoming call's date, time, and caller id. The pattern should match
+     * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+     *
+     * An usable pattern should look like this:
+     * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+     * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+            "missed_incoming_call_sms_pattern_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -4057,6 +4081,9 @@
         sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index e4198d1..d672c77 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -54,6 +55,8 @@
     private final int mEarfcn;
     // cell bandwidth, in kHz
     private final int mBandwidth;
+    // cell bands
+    private final List<Integer> mBands;
 
     // a list of additional PLMN-IDs reported for this cell
     private final ArraySet<String> mAdditionalPlmns;
@@ -70,6 +73,7 @@
         mPci = CellInfo.UNAVAILABLE;
         mTac = CellInfo.UNAVAILABLE;
         mEarfcn = CellInfo.UNAVAILABLE;
+        mBands = Collections.emptyList();
         mBandwidth = CellInfo.UNAVAILABLE;
         mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
@@ -87,8 +91,9 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
-                String.valueOf(mnc), null, null, new ArraySet<>(), null);
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, Collections.emptyList(), CellInfo.UNAVAILABLE,
+                String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(),
+                null);
     }
 
     /**
@@ -107,7 +112,7 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, List<Integer> bands, int bandwidth,
             @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
             @Nullable String alphas, @NonNull Collection<String> additionalPlmns,
             @Nullable ClosedSubscriberGroupInfo csgInfo) {
@@ -116,6 +121,7 @@
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
+        mBands = new ArrayList<>(bands);
         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
         mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
@@ -128,28 +134,28 @@
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
-        this(cid.ci, cid.pci, cid.tac, cid.earfcn,
+        this(cid.ci, cid.pci, cid.tac, cid.earfcn, Collections.emptyList(),
                 CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
-        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
-                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, Collections.emptyList(),
+                cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
                 cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) {
         this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
-                cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
+                cid.bands, cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
                 cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort,
                 cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
                         ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
     }
 
     private CellIdentityLte(@NonNull CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
 
@@ -157,7 +163,7 @@
     @Override
     public @NonNull CellIdentityLte sanitizeLocationInfo() {
         return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE,
                 mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
     }
 
@@ -226,8 +232,7 @@
      */
     @NonNull
     public List<Integer> getBands() {
-        // Todo: Add actual support
-        return Collections.emptyList();
+        return Collections.unmodifiableList(mBands);
     }
 
     /**
@@ -343,6 +348,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBands=").append(mBands)
         .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
@@ -362,6 +368,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
+        dest.writeList(mBands);
         dest.writeInt(mBandwidth);
         dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
@@ -374,6 +381,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
+        mBands = in.readArrayList(null);
         mBandwidth = in.readInt();
         mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index d4e34f9..f6dcff4 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -53,6 +53,7 @@
 
     private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
     private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+    private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS";
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
@@ -93,6 +94,7 @@
                     REASON_EXPLICIT_HEALTH_CHECK, null));
             assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                     null, null));
+            assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
         } finally {
             // Reconnect internet again so we won't break tests which assume internet available
             getDevice().executeShellCommand("svc wifi enable");
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 43759cf..4afebb5 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -76,10 +76,10 @@
 
     private static final String REASON_APP_CRASH = "REASON_APP_CRASH";
     private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH";
-    private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK";
 
     private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
     private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+    private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS";
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
@@ -146,6 +146,7 @@
                 REASON_APP_CRASH, TESTAPP_A));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -179,6 +180,7 @@
                         REASON_NATIVE_CRASH, null));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -219,6 +221,7 @@
                         REASON_NATIVE_CRASH, null));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
@@ -290,6 +293,7 @@
                 REASON_APP_CRASH, TESTAPP_A));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 26916bc..aed62d0 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,7 @@
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",
+            "android.view.WindowMetricsTest"
     };
 
     public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index ded4b91..786223a 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -415,7 +415,7 @@
         }
 
         const SectionFlags s = getSectionFlags(field);
-        if (s.userdebug_and_eng_only()) {
+        if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
             printf("#if ALLOW_RESTRICTED_SECTIONS\n");
         }
 
@@ -449,8 +449,13 @@
                 printf("    new TombstoneSection(%d, \"%s\"),\n", field->number(),
                         s.args().c_str());
                 break;
+            case SECTION_TEXT_DUMPSYS:
+                printf("    new TextDumpsysSection(%d, ", field->number());
+                splitAndPrint(s.args());
+                printf(" NULL),\n");
+                break;
         }
-        if (s.userdebug_and_eng_only()) {
+        if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
             printf("#endif\n");
         }
     }
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 0c6cf1c..e253d6d 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -132,27 +132,68 @@
     ],
 }
 
-droidstubs {
-    name: "framework-wifi-stubs-srcs",
+stubs_defaults {
+    name: "framework-wifi-stubs-srcs-defaults",
     srcs: [
-        ":framework-annotations",
         ":framework-wifi-updatable-sources",
+        ":framework-wifi-util-lib-aidls",
     ],
-    // This is needed as IOnWifiActivityEnergyInfoListener.aidl in framework-wifi depends on
-    // WifiActivityEnergyInfo.aidl in framework-minus-apex
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
-    defaults: [ "framework-module-stubs-defaults-systemapi" ],
-    sdk_version: "core_current",
-    libs: ["android_system_stubs_current"],
+    libs: [ "framework-annotations-lib" ],
+    sdk_version: "module_current",
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
 }
 
 java_library {
-    name: "framework-wifi-stubs",
-    srcs: [":framework-wifi-stubs-srcs"],
-    sdk_version: "core_current",
-    libs: ["android_system_stubs_current"],
+    name: "framework-wifi-stubs-publicapi",
+    srcs: [":framework-wifi-stubs-srcs-publicapi"],
+    sdk_version: "module_current",
+    installable: false,
+}
+
+java_library {
+    name: "framework-wifi-stubs-systemapi",
+    srcs: [":framework-wifi-stubs-srcs-systemapi"],
+    sdk_version: "module_current",
+    libs: ["framework-annotations-lib"],
+    installable: false,
+}
+
+java_library {
+    name: "framework-wifi-stubs-module_libs_api",
+    srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
+    sdk_version: "module_current",
+    libs: ["framework-annotations-lib"],
     installable: false,
 }
 
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index a831984..18b26db 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -100,12 +100,12 @@
     }
 
     /**
-     * Returns true when feature supported, otherwise false.
+     * Returns true when all of the queried features are supported, otherwise false.
      *
-     * @param feature one of feature from {@link HotspotFeatures}
+     * @param features One or combination of the features from {@link @HotspotFeatures}
      */
-    public boolean isFeatureSupported(@HotspotFeatures long feature) {
-        return (mSupportedFeatures & feature) == feature;
+    public boolean areFeaturesSupported(@HotspotFeatures long features) {
+        return (mSupportedFeatures & features) == features;
     }
 
     /**
@@ -122,7 +122,7 @@
      * Constructor with combination of the feature.
      * Zero to no supported feature.
      *
-     * @param features One or combination of the feature from {@link @HotspotFeatures}.
+     * @param features One or combination of the features from {@link @HotspotFeatures}.
      * @hide
      */
     public SoftApCapability(@HotspotFeatures long features) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index ae5bf7d..2b47623 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -762,7 +762,8 @@
          * {@link #setBand(@BandType int)}.
          *
          * The channel auto selection will offload to driver when
-         * {@link SoftApCapability#isFeatureSupported(SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
+         * {@link SoftApCapability#areFeaturesSupported(
+         * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
          * return true. Driver will auto select best channel which based on environment
          * interference to get best performance. Check {@link SoftApCapability} to get more detail.
          *
@@ -807,7 +808,7 @@
          *
          * <p>
          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
-         * {@link SoftApCapability#isFeatureSupported(int)}
+         * {@link SoftApCapability#areFeaturesSupported(int)}
          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
          * or not this feature is supported.
          *
@@ -882,7 +883,7 @@
          * <p>
          * This method requires hardware support. Hardware support can be determined using
          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
-         * {@link SoftApCapability#isFeatureSupported(int)}
+         * {@link SoftApCapability#areFeaturesSupported(int)}
          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
          *
          * <p>