Merge "Verify that b/68859852 won't happen again"
diff --git a/Android.mk b/Android.mk
index 7a3c46d..06f64f1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -219,6 +219,8 @@
 	core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
 	core/java/android/hardware/location/IContextHubCallback.aidl \
+	core/java/android/hardware/location/IContextHubClient.aidl \
+	core/java/android/hardware/location/IContextHubClientCallback.aidl \
 	core/java/android/hardware/location/IContextHubService.aidl \
 	core/java/android/hardware/location/IContextHubTransactionCallback.aidl \
 	core/java/android/hardware/radio/IRadioService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 00a9452..2f3fc26 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -343,6 +343,7 @@
     field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
     field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
     field public static final int buttonBarStyle = 16843566; // 0x101032e
+    field public static final int buttonCornerRadius = 16844149; // 0x1010575
     field public static final int buttonGravity = 16844030; // 0x10104fe
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
@@ -47905,6 +47906,7 @@
     method public void interrupt();
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isEnabled();
+    method public boolean isObservedEventType(int);
     method public boolean isTouchExplorationEnabled();
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
diff --git a/api/test-current.txt b/api/test-current.txt
index 8647ed3..b181538 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -958,6 +958,15 @@
 
 package android.view.accessibility {
 
+  public final class AccessibilityManager {
+    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+  }
+
+  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+  }
+
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
   }
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..e0d60cd 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.accessibilityservice;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -47,8 +49,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -554,7 +554,7 @@
     }
 
     /**
-     * Updates the properties that an AccessibilitySerivice can change dynamically.
+     * Updates the properties that an AccessibilityService can change dynamically.
      *
      * @param other The info from which to update the properties.
      *
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 6ea0825..8c47598 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -34,8 +34,6 @@
 
 import libcore.io.IoUtils;
 
-import com.google.android.collect.Maps;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedInputStream;
@@ -139,7 +137,7 @@
             Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
         }
 
-        Map map = null;
+        Map<String, Object> map = null;
         StructStat stat = null;
         try {
             stat = Os.stat(mFile.getPath());
@@ -148,7 +146,7 @@
                 try {
                     str = new BufferedInputStream(
                             new FileInputStream(mFile), 16*1024);
-                    map = XmlUtils.readMapXml(str);
+                    map = (Map<String, Object>) XmlUtils.readMapXml(str);
                 } catch (Exception e) {
                     Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
                 } finally {
@@ -214,12 +212,14 @@
         }
     }
 
+    @Override
     public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
         synchronized(mLock) {
             mListeners.put(listener, CONTENT);
         }
     }
 
+    @Override
     public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
         synchronized(mLock) {
             mListeners.remove(listener);
@@ -241,6 +241,7 @@
         }
     }
 
+    @Override
     public Map<String, ?> getAll() {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -249,6 +250,7 @@
         }
     }
 
+    @Override
     @Nullable
     public String getString(String key, @Nullable String defValue) {
         synchronized (mLock) {
@@ -258,6 +260,7 @@
         }
     }
 
+    @Override
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
         synchronized (mLock) {
@@ -267,6 +270,7 @@
         }
     }
 
+    @Override
     public int getInt(String key, int defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -274,6 +278,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public long getLong(String key, long defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -281,6 +286,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public float getFloat(String key, float defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -288,6 +294,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public boolean getBoolean(String key, boolean defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -296,6 +303,7 @@
         }
     }
 
+    @Override
     public boolean contains(String key) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -303,6 +311,7 @@
         }
     }
 
+    @Override
     public Editor edit() {
         // TODO: remove the need to call awaitLoadedLocked() when
         // requesting an editor.  will require some work on the
@@ -347,71 +356,81 @@
     }
 
     public final class EditorImpl implements Editor {
-        private final Object mLock = new Object();
+        private final Object mEditorLock = new Object();
 
-        @GuardedBy("mLock")
-        private final Map<String, Object> mModified = Maps.newHashMap();
+        @GuardedBy("mEditorLock")
+        private final Map<String, Object> mModified = new HashMap<>();
 
-        @GuardedBy("mLock")
+        @GuardedBy("mEditorLock")
         private boolean mClear = false;
 
+        @Override
         public Editor putString(String key, @Nullable String value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putStringSet(String key, @Nullable Set<String> values) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key,
                         (values == null) ? null : new HashSet<String>(values));
                 return this;
             }
         }
+        @Override
         public Editor putInt(String key, int value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putLong(String key, long value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putFloat(String key, float value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putBoolean(String key, boolean value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
 
+        @Override
         public Editor remove(String key) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, this);
                 return this;
             }
         }
 
+        @Override
         public Editor clear() {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mClear = true;
                 return this;
             }
         }
 
+        @Override
         public void apply() {
             final long startTime = System.currentTimeMillis();
 
             final MemoryCommitResult mcr = commitToMemory();
             final Runnable awaitCommit = new Runnable() {
+                    @Override
                     public void run() {
                         try {
                             mcr.writtenToDiskLatch.await();
@@ -429,6 +448,7 @@
             QueuedWork.addFinisher(awaitCommit);
 
             Runnable postWriteRunnable = new Runnable() {
+                    @Override
                     public void run() {
                         awaitCommit.run();
                         QueuedWork.removeFinisher(awaitCommit);
@@ -471,13 +491,13 @@
                     listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
                 }
 
-                synchronized (mLock) {
+                synchronized (mEditorLock) {
                     boolean changesMade = false;
 
                     if (mClear) {
-                        if (!mMap.isEmpty()) {
+                        if (!mapToWriteToDisk.isEmpty()) {
                             changesMade = true;
-                            mMap.clear();
+                            mapToWriteToDisk.clear();
                         }
                         mClear = false;
                     }
@@ -489,18 +509,18 @@
                         // setting a value to "null" for a given key is specified to be
                         // equivalent to calling remove on that key.
                         if (v == this || v == null) {
-                            if (!mMap.containsKey(k)) {
+                            if (!mapToWriteToDisk.containsKey(k)) {
                                 continue;
                             }
-                            mMap.remove(k);
+                            mapToWriteToDisk.remove(k);
                         } else {
-                            if (mMap.containsKey(k)) {
-                                Object existingValue = mMap.get(k);
+                            if (mapToWriteToDisk.containsKey(k)) {
+                                Object existingValue = mapToWriteToDisk.get(k);
                                 if (existingValue != null && existingValue.equals(v)) {
                                     continue;
                                 }
                             }
-                            mMap.put(k, v);
+                            mapToWriteToDisk.put(k, v);
                         }
 
                         changesMade = true;
@@ -522,6 +542,7 @@
                     mapToWriteToDisk);
         }
 
+        @Override
         public boolean commit() {
             long startTime = 0;
 
@@ -564,11 +585,7 @@
                 }
             } else {
                 // Run this function on the main thread.
-                ActivityThread.sMainThreadHandler.post(new Runnable() {
-                        public void run() {
-                            notifyListeners(mcr);
-                        }
-                    });
+                ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr));
             }
         }
     }
@@ -594,6 +611,7 @@
         final boolean isFromSyncCommit = (postWriteRunnable == null);
 
         final Runnable writeToDiskRunnable = new Runnable() {
+                @Override
                 public void run() {
                     synchronized (mWritingToDiskLock) {
                         writeToFile(mcr, isFromSyncCommit);
@@ -646,7 +664,7 @@
         return str;
     }
 
-    // Note: must hold mWritingToDiskLock
+    @GuardedBy("mWritingToDiskLock")
     private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
         long startTime = 0;
         long existsTime = 0;
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 8d81199..cdeee35 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -216,7 +216,7 @@
      * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
      */
     public Slice getSlice() {
-        if (getFormat() == FORMAT_ACTION) {
+        if (FORMAT_ACTION.equals(getFormat())) {
             return ((Pair<PendingIntent, Slice>) mObj).second;
         }
         return (Slice) mObj;
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index b7e353a..52527ed 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -16,43 +16,48 @@
 package android.hardware.location;
 
 import android.annotation.RequiresPermission;
-import android.os.Handler;
+import android.os.RemoteException;
+
+import dalvik.system.CloseGuard;
 
 import java.io.Closeable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A class describing a client of the Context Hub Service.
  *
- * Clients can send messages to nanoapps at a Context Hub through this object.
+ * Clients can send messages to nanoapps at a Context Hub through this object. The APIs supported
+ * by this object are thread-safe and can be used without external synchronization.
  *
  * @hide
  */
 public class ContextHubClient implements Closeable {
     /*
-     * The ContextHubClient interface associated with this client.
+     * The proxy to the client interface at the service.
      */
-    // TODO: Implement this interface and associate with ContextHubClient object
-    // private final IContextHubClient mClientInterface;
+    private final IContextHubClient mClientProxy;
 
     /*
-     * The listening callback associated with this client.
+     * The callback interface associated with this client.
      */
-    private ContextHubClientCallback mCallback;
+    private final IContextHubClientCallback mCallbackInterface;
 
     /*
      * The Context Hub that this client is attached to.
      */
-    private ContextHubInfo mAttachedHub;
+    private final ContextHubInfo mAttachedHub;
 
-    /*
-     * The handler to invoke mCallback.
-     */
-    private Handler mCallbackHandler;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
-    ContextHubClient(ContextHubClientCallback callback, Handler handler, ContextHubInfo hubInfo) {
-        mCallback = callback;
-        mCallbackHandler = handler;
+    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+    /* package */ ContextHubClient(
+            IContextHubClient clientProxy, IContextHubClientCallback callback,
+            ContextHubInfo hubInfo) {
+        mClientProxy = clientProxy;
+        mCallbackInterface = callback;
         mAttachedHub = hubInfo;
+        mCloseGuard.open("close");
     }
 
     /**
@@ -71,7 +76,14 @@
      * All futures messages targeted for this client are dropped at the service.
      */
     public void close() {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        if (!mIsClosed.getAndSet(true)) {
+            mCloseGuard.close();
+            try {
+                mClientProxy.close();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
@@ -90,6 +102,22 @@
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @ContextHubTransaction.Result
     public int sendMessageToNanoApp(NanoAppMessage message) {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        try {
+            return mClientProxy.sendMessageToNanoApp(message);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 2411727..b31c7bc 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -456,6 +456,54 @@
     }
 
     /**
+     * Creates an interface to the ContextHubClient to send down to the service.
+     *
+     * @param callback the callback to invoke at the client process
+     * @param handler the handler to post callbacks for this client
+     *
+     * @return the callback interface
+     */
+    private IContextHubClientCallback createClientCallback(
+            ContextHubClientCallback callback, Handler handler) {
+        return new IContextHubClientCallback.Stub() {
+            @Override
+            public void onMessageFromNanoApp(NanoAppMessage message) {
+                handler.post(() -> callback.onMessageFromNanoApp(message));
+            }
+
+            @Override
+            public void onHubReset() {
+                handler.post(() -> callback.onHubReset());
+            }
+
+            @Override
+            public void onNanoAppAborted(long nanoAppId, int abortCode) {
+                handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode));
+            }
+
+            @Override
+            public void onNanoAppLoaded(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppLoaded(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppUnloaded(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppUnloaded(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppEnabled(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppEnabled(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppDisabled(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppDisabled(nanoAppId));
+            }
+        };
+    }
+
+    /**
      * Creates and registers a client and its callback with the Context Hub Service.
      *
      * A client is registered with the Context Hub Service for a specified Context Hub. When the
@@ -463,19 +511,37 @@
      * {@link ContextHubClient} object, and receive notifications through the provided callback.
      *
      * @param callback the notification callback to register
-     * @param hubInfo the hub to attach this client to
-     * @param handler the handler to invoke the callback, if null uses the main thread's Looper
-     *
+     * @param hubInfo  the hub to attach this client to
+     * @param handler  the handler to invoke the callback, if null uses the main thread's Looper
      * @return the registered client object
      *
-     * @see ContextHubClientCallback
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws NullPointerException     if callback or hubInfo is null
      *
      * @hide
+     * @see ContextHubClientCallback
      */
     public ContextHubClient createClient(
             ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) {
-        throw new UnsupportedOperationException(
-                "TODO: Implement this, and throw an exception on error");
+        if (callback == null) {
+            throw new NullPointerException("Callback cannot be null");
+        }
+        if (hubInfo == null) {
+            throw new NullPointerException("Hub info cannot be null");
+        }
+
+        Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler;
+        IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler);
+
+        IContextHubClient client;
+        try {
+            client = mService.createClient(clientInterface, hubInfo.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return new ContextHubClient(client, clientInterface, hubInfo);
     }
 
     /**
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
new file mode 100644
index 0000000..d81126a
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * @hide
+ */
+interface IContextHubClient {
+
+    // Sends a message to a nanoapp
+    int sendMessageToNanoApp(in NanoAppMessage message);
+
+    // Closes the connection with the Context Hub
+    void close();
+}
diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl
new file mode 100644
index 0000000..1c76bcb
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * An interface used by the Context Hub Service to invoke callbacks for lifecycle notifications of a
+ * Context Hub and nanoapps, as well as for nanoapp messaging.
+ *
+ * @hide
+ */
+oneway interface IContextHubClientCallback {
+
+    // Callback invoked when receiving a message from a nanoapp.
+    void onMessageFromNanoApp(in NanoAppMessage message);
+
+    // Callback invoked when the attached Context Hub has reset.
+    void onHubReset();
+
+    // Callback invoked when a nanoapp aborts at the attached Context Hub.
+    void onNanoAppAborted(long nanoAppId, int abortCode);
+
+    // Callback invoked when a nanoapp is loaded at the attached Context Hub.
+    void onNanoAppLoaded(long nanoAppId);
+
+    // Callback invoked when a nanoapp is unloaded from the attached Context Hub.
+    void onNanoAppUnloaded(long nanoAppId);
+
+    // Callback invoked when a nanoapp is enabled at the attached Context Hub.
+    void onNanoAppEnabled(long nanoAppId);
+
+    // Callback invoked when a nanoapp is disabled at the attached Context Hub.
+    void onNanoAppDisabled(long nanoAppId);
+}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ff8c1d0..1bb7c8f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,12 +17,14 @@
 package android.hardware.location;
 
 // Declare any non-default types here with import statements
-import android.hardware.location.ContextHubMessage;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubMessage;
 import android.hardware.location.NanoApp;
-import android.hardware.location.NanoAppInstanceInfo;
 import android.hardware.location.NanoAppFilter;
+import android.hardware.location.NanoAppInstanceInfo;
 import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
 
 /**
  * @hide
@@ -52,4 +54,7 @@
 
     // send a message to a nanoApp
     int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg);
+
+    // Creates a client to send and receive messages
+    IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
 }
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 13b9206..7836cd0 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,18 +21,16 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.Paint.Align;
 import android.graphics.Region.Op;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.Keyboard.Key;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.GestureDetector;
@@ -986,6 +984,9 @@
 
     private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) {
         if (mAccessibilityManager.isEnabled()) {
+            if (!mAccessibilityManager.isObservedEventType(eventType)) {
+                return;
+            }
             AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
             onInitializeAccessibilityEvent(event);
             final String text;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 729c0ff..1e0948a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10456,6 +10456,15 @@
                 "storage_settings_clobber_threshold";
 
         /**
+         * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF}
+         * temporarily for all users.
+         *
+         * @hide
+         */
+        public static final String LOCATION_GLOBAL_KILL_SWITCH =
+                "location_global_kill_switch";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 345e300..0525ab1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7208,7 +7208,8 @@
      * @param text The announcement text.
      */
     public void announceForAccessibility(CharSequence text) {
-        if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
+        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) {
             AccessibilityEvent event = AccessibilityEvent.obtain(
                     AccessibilityEvent.TYPE_ANNOUNCEMENT);
             onInitializeAccessibilityEvent(event);
@@ -10967,7 +10968,8 @@
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
                 event.setAction(action);
@@ -11792,7 +11794,8 @@
 
     private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
             int fromIndex, int toIndex) {
-        if (mParent == null) {
+        if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) {
             return;
         }
         AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -26182,7 +26185,8 @@
 
         @Override
         public void run() {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_SCROLLED);
                 event.setScrollDeltaX(mDeltaX);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 5adea66..1d19a9f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -23,6 +24,8 @@
 
 import com.android.internal.util.BitUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -709,6 +712,38 @@
      */
     public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
 
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_VIEW_CLICKED,
+            TYPE_VIEW_LONG_CLICKED,
+            TYPE_VIEW_SELECTED,
+            TYPE_VIEW_FOCUSED,
+            TYPE_VIEW_TEXT_CHANGED,
+            TYPE_WINDOW_STATE_CHANGED,
+            TYPE_NOTIFICATION_STATE_CHANGED,
+            TYPE_VIEW_HOVER_ENTER,
+            TYPE_VIEW_HOVER_EXIT,
+            TYPE_TOUCH_EXPLORATION_GESTURE_START,
+            TYPE_TOUCH_EXPLORATION_GESTURE_END,
+            TYPE_WINDOW_CONTENT_CHANGED,
+            TYPE_VIEW_SCROLLED,
+            TYPE_VIEW_TEXT_SELECTION_CHANGED,
+            TYPE_ANNOUNCEMENT,
+            TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+            TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+            TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+            TYPE_GESTURE_DETECTION_START,
+            TYPE_GESTURE_DETECTION_END,
+            TYPE_TOUCH_INTERACTION_START,
+            TYPE_TOUCH_INTERACTION_END,
+            TYPE_WINDOWS_CHANGED,
+            TYPE_VIEW_CONTEXT_CLICKED,
+            TYPE_ASSIST_READING_CONTEXT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {}
+
     /**
      * Mask for {@link AccessibilityEvent} all types.
      *
@@ -741,7 +776,7 @@
     private static final SynchronizedPool<AccessibilityEvent> sPool =
             new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
 
-    private int mEventType;
+    private @EventType int mEventType;
     private CharSequence mPackageName;
     private long mEventTime;
     int mMovementGranularity;
@@ -833,7 +868,7 @@
      *
      * @return The event type.
      */
-    public int getEventType() {
+    public @EventType int getEventType() {
         return mEventType;
     }
 
@@ -890,7 +925,7 @@
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
-    public void setEventType(int eventType) {
+    public void setEventType(@EventType int eventType) {
         enforceNotSealed();
         mEventType = eventType;
     }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 35f6acb..0375635 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -24,6 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -187,6 +188,7 @@
      *
      * @hide
      */
+    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -452,6 +454,18 @@
     }
 
     /**
+     * Returns whether there are observers registered for this event type. If
+     * this method returns false you shuold not generate events of this type
+     * to conserve resources.
+     *
+     * @param type The event type.
+     * @return Whether the event is being observed.
+     */
+    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+        return mIsEnabled && (mRelevantEventTypes & type) != 0;
+    }
+
+    /**
      * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
@@ -683,6 +697,7 @@
      *                for a callback on the process's main handler.
      * @hide
      */
+    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -698,6 +713,7 @@
      *
      * @hide
      */
+    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         // Final CopyOnWriteArrayList - no lock needed.
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4d3189e..b379280 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1952,7 +1952,8 @@
             CharSequence beforeText = mInputText.getText();
             if (!text.equals(beforeText.toString())) {
                 mInputText.setText(text);
-                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                        AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
                     mInputText.onInitializeAccessibilityEvent(event);
@@ -2612,7 +2613,7 @@
         }
 
         private void sendAccessibilityEventForVirtualText(int eventType) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 mInputText.onInitializeAccessibilityEvent(event);
                 mInputText.onPopulateAccessibilityEvent(event);
@@ -2623,7 +2624,7 @@
 
         private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
                 String text) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 event.setClassName(Button.class.getName());
                 event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d9bc51f..71532a7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10836,6 +10836,10 @@
 
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
+            return;
+        }
         AccessibilityEvent event =
                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
         event.setFromIndex(fromIndex);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index d807120..bfde6ac 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -504,7 +504,8 @@
         private void trySendAccessibilityEvent() {
             AccessibilityManager accessibilityManager =
                     AccessibilityManager.getInstance(mView.getContext());
-            if (!accessibilityManager.isEnabled()) {
+            if (!accessibilityManager.isObservedEventType(
+                    AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
                 return;
             }
             // treat toasts as notifications since they are used to
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index bab0306aa..5ec9094 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -113,6 +113,11 @@
     void showGlobalActionsMenu();
 
     /**
+     * Notifies the status bar that a new rotation suggestion is available.
+     */
+    void onProposedRotationChanged(int rotation);
+
+    /**
      * Set whether the top app currently hides the statusbar.
      *
      * @param hidesStatusBar whether it is being hidden
diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java
index c22be2c..9a6e542 100644
--- a/core/java/com/android/internal/util/RingBuffer.java
+++ b/core/java/com/android/internal/util/RingBuffer.java
@@ -60,6 +60,25 @@
         mBuffer[indexOf(mCursor++)] = t;
     }
 
+    /**
+     * Returns object of type <T> at the next writable slot, creating one if it is not already
+     * available. In case of any errors while creating the object, <code>null</code> will
+     * be returned.
+     */
+    public T getNextSlot() {
+        final int nextSlotIdx = indexOf(mCursor++);
+        T item = mBuffer[nextSlotIdx];
+        if (item == null) {
+            try {
+                item = (T) mBuffer.getClass().getComponentType().newInstance();
+            } catch (IllegalAccessException | InstantiationException e) {
+                return null;
+            }
+            mBuffer[nextSlotIdx] = item;
+        }
+        return item;
+    }
+
     public T[] toArray() {
         // Only generic way to create a T[] from another T[]
         T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass());
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 50ad547..759a41a 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -186,6 +186,9 @@
         }
 
         final AccessibilityEvent event = createEvent(virtualViewId, eventType);
+        if (event == null) {
+            return false;
+        }
         return parent.requestSendAccessibilityEvent(mView, event);
     }
 
@@ -240,6 +243,9 @@
             if (parent != null) {
                 final AccessibilityEvent event = createEvent(virtualViewId,
                         AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                if (event == null) {
+                    return;
+                }
                 event.setContentChangeTypes(changeTypes);
                 parent.requestSendAccessibilityEvent(mView, event);
             }
@@ -305,6 +311,9 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForHost(int eventType) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            return null;
+        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         mView.onInitializeAccessibilityEvent(event);
 
@@ -325,6 +334,9 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            return null;
+        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setEnabled(true);
         event.setClassName(DEFAULT_CLASS_NAME);
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index c3c5760..7ba21e8 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -23,7 +23,7 @@
         <item>
             <shape android:shape="rectangle"
                    android:tint="@color/btn_colored_background_material">
-                <corners android:radius="@dimen/control_corner_material" />
+                <corners android:radius="?attr/buttonCornerRadius" />
                 <solid android:color="@color/white" />
                 <padding android:left="@dimen/button_padding_horizontal_material"
                          android:top="@dimen/button_padding_vertical_material"
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
index 8a31d5e..9d9cd06 100644
--- a/core/res/res/drawable/btn_default_mtrl_shape.xml
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -23,7 +23,7 @@
        android:insetBottom="@dimen/button_inset_vertical_material">
     <shape android:shape="rectangle"
            android:tint="?attr/colorButtonNormal">
-        <corners android:radius="@dimen/control_corner_material" />
+        <corners android:radius="?attr/buttonCornerRadius" />
         <solid android:color="@color/white" />
         <padding android:left="@dimen/button_padding_horizontal_material"
                  android:top="@dimen/button_padding_vertical_material"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9a0eafd..da8a9cc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1016,6 +1016,9 @@
         <!-- Style for the "neutral" buttons within button bars. -->
         <attr name="buttonBarNeutralButtonStyle" format="reference" />
 
+        <!-- Corner radius of buttons. -->
+        <attr name="buttonCornerRadius" format="dimension" />
+
         <!-- Style for the search query widget. -->
         <attr name="searchViewStyle" format="reference" />
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 65a7ec6..171f74f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3174,6 +3174,8 @@
 
     <!-- Corner radius of system dialogs -->
     <dimen name="config_dialogCornerRadius">2dp</dimen>
+    <!-- Corner radius of system buttons -->
+    <dimen name="config_buttonCornerRadius">@dimen/control_corner_material</dimen>
     <!-- Controls whether system buttons use all caps for text -->
     <bool name="config_buttonTextAllCaps">true</bool>
     <!-- Name of the font family used for system buttons -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e370786..3dbb89c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2853,6 +2853,7 @@
       <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
       <public name="compileSdkVersionCodename" />
       <public name="screenReaderFocusable" />
+      <public name="buttonCornerRadius" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 3fa0d3b..39310a8 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -69,6 +69,7 @@
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonStyle">@style/Widget.DeviceDefault.Button</item>
 
         <item name="buttonStyleSmall">@style/Widget.DeviceDefault.Button.Small</item>
@@ -224,6 +225,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -243,6 +245,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -264,6 +267,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -284,6 +288,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -303,6 +308,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Color palette -->
@@ -331,6 +337,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -349,6 +356,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -368,6 +376,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -403,6 +412,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -423,6 +433,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -441,6 +452,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -461,6 +473,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -480,6 +493,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -499,6 +513,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -518,6 +533,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -537,6 +553,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -556,6 +573,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -573,6 +591,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -590,6 +609,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -614,6 +634,7 @@
         <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonStyle">@style/Widget.DeviceDefault.Light.Button</item>
 
         <item name="buttonStyleSmall">@style/Widget.DeviceDefault.Light.Button.Small</item>
@@ -762,6 +783,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -780,6 +802,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -799,6 +822,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -820,6 +844,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -840,6 +865,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -855,6 +881,7 @@
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar.AlertDialog</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
         <item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Light.Button.Borderless.Small</item>
@@ -885,6 +912,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -903,6 +931,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -922,6 +951,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -967,6 +997,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -987,6 +1018,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1005,6 +1037,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1025,6 +1058,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1044,6 +1078,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1061,6 +1096,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1078,6 +1114,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1088,6 +1125,7 @@
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
+        <item name="colorBackground">@color/background_device_default_light</item>
         <item name="colorPrimary">@color/primary_device_default_settings_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
@@ -1107,6 +1145,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1125,6 +1164,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1153,6 +1193,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1171,6 +1212,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1189,6 +1231,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
@@ -1207,6 +1250,7 @@
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
 
         <!-- Button styles -->
+        <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
     </style>
 
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index c317121..9e6b1ab 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -103,6 +103,7 @@
         <item name="buttonStyleSmall">@style/Widget.Material.Button.Small</item>
         <item name="buttonStyleInset">@style/Widget.Material.Button.Inset</item>
         <item name="buttonStyleToggle">@style/Widget.Material.Button.Toggle</item>
+        <item name="buttonCornerRadius">@dimen/control_corner_material</item>
 
         <item name="switchStyle">@style/Widget.Material.CompoundButton.Switch</item>
         <item name="mediaRouteButtonStyle">@style/Widget.Material.MediaRouteButton</item>
@@ -472,6 +473,7 @@
         <item name="buttonStyleSmall">@style/Widget.Material.Light.Button.Small</item>
         <item name="buttonStyleInset">@style/Widget.Material.Light.Button.Inset</item>
         <item name="buttonStyleToggle">@style/Widget.Material.Light.Button.Toggle</item>
+        <item name="buttonCornerRadius">@dimen/control_corner_material</item>
 
         <item name="switchStyle">@style/Widget.Material.Light.CompoundButton.Switch</item>
         <item name="mediaRouteButtonStyle">@style/Widget.Material.Light.MediaRouteButton</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 454d209..4ce6029 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -231,6 +231,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
                     Settings.Global.LOW_BATTERY_SOUND,
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 83c82af..94a05b2 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -427,7 +427,6 @@
   ATRACE_CALL();
   constexpr const int kMaxIterations = 20;
 
-  *out_last_reference = 0u;
   for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
                               in_out_value->data != 0u && iteration < kMaxIterations;
        iteration++) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a77c4b9..b033137 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -210,7 +210,7 @@
   // are OR'd together with `in_out_flags`.
   // `in_out_config` is populated with the configuration for which the resolved value was defined.
   // `out_last_reference` is populated with the last reference ID before resolving to an actual
-  // value.
+  // value. This is only initialized if the passed in `in_out_value` is a reference.
   // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
   // it was not found.
   ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index ab1a22e..567adfe 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -319,7 +319,7 @@
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
   EXPECT_EQ(basic::R::integer::ref2, value.data);
 
-  uint32_t last_ref;
+  uint32_t last_ref = 0u;
   cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
   ASSERT_NE(kInvalidCookie, cookie);
   EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
@@ -342,7 +342,7 @@
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
   EXPECT_EQ(basic::R::array::integerArray1, value.data);
 
-  uint32_t last_ref;
+  uint32_t last_ref = 0u;
   cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
   ASSERT_NE(kInvalidCookie, cookie);
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
@@ -350,6 +350,25 @@
   EXPECT_EQ(basic::R::array::integerArray1, last_ref);
 }
 
+TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  ResTable_config selected_config;
+  memset(&selected_config, 0, sizeof(selected_config));
+
+  uint32_t flags = 0u;
+
+  // Create some kind of Res_value that is NOT a reference.
+  Res_value value;
+  value.dataType = Res_value::TYPE_STRING;
+  value.data = 0;
+
+  uint32_t last_ref = basic::R::string::test1;
+  EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
+  EXPECT_EQ(basic::R::string::test1, last_ref);
+}
+
 static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
                                    const ResTable_config& configuration) {
   return configurations.count(configuration) > 0;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 46d4ae7..845acc0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -110,7 +110,8 @@
     // We only respect the nothingToDraw check when we are composing a layer. This
     // ensures that we paint the layer even if it is not currently visible in the
     // event that the properties change and it becomes visible.
-    if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+    if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
+            (renderNode->nothingToDraw() && mComposeLayer)) {
         return;
     }
 
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 56a675a..15ea367 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -195,7 +195,12 @@
             if(!handleObexPacket(packet)) {
                 return;
             }
-            if (!mHasBody) {
+            /* Don't Pre-Send continue when Remote requested for SRM
+             * Let the Application confirm.
+             */
+            if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+                    + " not hasBody case: " + mHasBody);
+            if (!mHasBody && !mSrmEnabled) {
                 while ((!mGetOperation) && (!finalBitSet)) {
                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                     if (mPrivateInput.available() > 0) {
@@ -204,8 +209,13 @@
                 }
             }
         }
-
-        while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
+        /* Don't Pre-Send continue when Remote requested for SRM
+          * Let the Application confirm.
+          */
+        if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+            + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
+        while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
+                && (mPrivateInput.available() == 0)) {
             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
             if (mPrivateInput.available() > 0) {
                 break;
diff --git a/packages/SettingsLib/res/xml/timezones.xml b/packages/SettingsLib/res/xml/timezones.xml
index 12d31cf..6a8d780 100644
--- a/packages/SettingsLib/res/xml/timezones.xml
+++ b/packages/SettingsLib/res/xml/timezones.xml
@@ -27,6 +27,7 @@
     <timezone id="Atlantic/South_Georgia"></timezone>
     <timezone id="Atlantic/Azores"></timezone>
     <timezone id="Atlantic/Cape_Verde"></timezone>
+    <timezone id="Etc/UTC"></timezone>
     <timezone id="Africa/Casablanca"></timezone>
     <timezone id="Europe/London"></timezone>
     <timezone id="Europe/Amsterdam"></timezone>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index ee7885d..0703330 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -27,7 +26,6 @@
 import com.android.settingslib.applications.InterestingConfigChanges;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -104,10 +102,10 @@
         }
         for (int i = 0; i < mCategories.size(); i++) {
             DashboardCategory category = mCategories.get(i);
-            for (int j = 0; j < category.tiles.size(); j++) {
-                Tile tile = category.tiles.get(j);
+            for (int j = 0; j < category.getTilesCount(); j++) {
+                Tile tile = category.getTile(j);
                 if (tileBlacklist.contains(tile.intent.getComponent())) {
-                    category.tiles.remove(j--);
+                    category.removeTile(j--);
                 }
             }
         }
@@ -181,7 +179,7 @@
                         newCategory = new DashboardCategory();
                         categoryByKeyMap.put(newCategoryKey, newCategory);
                     }
-                    newCategory.tiles.add(tile);
+                    newCategory.addTile(tile);
                 }
             }
         }
@@ -198,7 +196,7 @@
     synchronized void sortCategories(Context context,
             Map<String, DashboardCategory> categoryByKeyMap) {
         for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
-            sortCategoriesForExternalTiles(context, categoryEntry.getValue());
+            categoryEntry.getValue().sortTiles(context.getPackageName());
         }
     }
 
@@ -210,16 +208,16 @@
     synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
         for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
             final DashboardCategory category = categoryEntry.getValue();
-            final int count = category.tiles.size();
+            final int count = category.getTilesCount();
             final Set<ComponentName> components = new ArraySet<>();
             for (int i = count - 1; i >= 0; i--) {
-                final Tile tile = category.tiles.get(i);
+                final Tile tile = category.getTile(i);
                 if (tile.intent == null) {
                     continue;
                 }
                 final ComponentName tileComponent = tile.intent.getComponent();
                 if (components.contains(tileComponent)) {
-                    category.tiles.remove(i);
+                    category.removeTile(i);
                 } else {
                     components.add(tileComponent);
                 }
@@ -234,28 +232,7 @@
      */
     private synchronized void sortCategoriesForExternalTiles(Context context,
             DashboardCategory dashboardCategory) {
-        final String skipPackageName = context.getPackageName();
+        dashboardCategory.sortTiles(context.getPackageName());
 
-        // Sort tiles based on [priority, package within priority]
-        Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
-            final String package1 = tile1.intent.getComponent().getPackageName();
-            final String package2 = tile2.intent.getComponent().getPackageName();
-            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
-            // First sort by priority
-            final int priorityCompare = tile2.priority - tile1.priority;
-            if (priorityCompare != 0) {
-                return priorityCompare;
-            }
-            // Then sort by package name, skip package take precedence
-            if (packageCompare != 0) {
-                if (TextUtils.equals(package1, skipPackageName)) {
-                    return -1;
-                }
-                if (TextUtils.equals(package2, skipPackageName)) {
-                    return 1;
-                }
-            }
-            return packageCompare;
-        });
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index f6f8168..a966e82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.drawer;
 
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -23,6 +25,8 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 public class DashboardCategory implements Parcelable {
@@ -48,39 +52,59 @@
     /**
      * List of the category's children
      */
-    public List<Tile> tiles = new ArrayList<>();
+    private List<Tile> mTiles = new ArrayList<>();
 
+    DashboardCategory(DashboardCategory in) {
+        if (in != null) {
+            title = in.title;
+            key = in.key;
+            priority = in.priority;
+            for (Tile tile : in.mTiles) {
+                mTiles.add(tile);
+            }
+        }
+    }
 
     public DashboardCategory() {
         // Empty
     }
 
+    /**
+     * Get a copy of the list of the category's children.
+     *
+     * Note: the returned list serves as a read-only list. If tiles needs to be added or removed
+     * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
+     */
+    public List<Tile> getTiles() {
+        return Collections.unmodifiableList(mTiles);
+    }
+
     public void addTile(Tile tile) {
-        tiles.add(tile);
+        mTiles.add(tile);
     }
 
     public void addTile(int n, Tile tile) {
-        tiles.add(n, tile);
+        mTiles.add(n, tile);
     }
 
     public void removeTile(Tile tile) {
-        tiles.remove(tile);
+        mTiles.remove(tile);
     }
 
     public void removeTile(int n) {
-        tiles.remove(n);
+        mTiles.remove(n);
     }
 
     public int getTilesCount() {
-        return tiles.size();
+        return mTiles.size();
     }
 
     public Tile getTile(int n) {
-        return tiles.get(n);
+        return mTiles.get(n);
     }
 
     public boolean containsComponent(ComponentName component) {
-        for (Tile tile : tiles) {
+        for (Tile tile : mTiles) {
             if (TextUtils.equals(tile.intent.getComponent().getClassName(),
                     component.getClassName())) {
                 if (DEBUG) {
@@ -95,6 +119,40 @@
         return false;
     }
 
+    /**
+     * Sort priority value for tiles in this category.
+     */
+    public void sortTiles() {
+        Collections.sort(mTiles, TILE_COMPARATOR);
+    }
+
+    /**
+     * Sort priority value and package name for tiles in this category.
+     */
+    public void sortTiles(String skipPackageName) {
+        // Sort mTiles based on [priority, package within priority]
+        Collections.sort(mTiles, (tile1, tile2) -> {
+            final String package1 = tile1.intent.getComponent().getPackageName();
+            final String package2 = tile2.intent.getComponent().getPackageName();
+            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
+            // First sort by priority
+            final int priorityCompare = tile2.priority - tile1.priority;
+            if (priorityCompare != 0) {
+                return priorityCompare;
+            }
+            // Then sort by package name, skip package take precedence
+            if (packageCompare != 0) {
+                if (TextUtils.equals(package1, skipPackageName)) {
+                    return -1;
+                }
+                if (TextUtils.equals(package2, skipPackageName)) {
+                    return 1;
+                }
+            }
+            return packageCompare;
+        });
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -106,11 +164,11 @@
         dest.writeString(key);
         dest.writeInt(priority);
 
-        final int count = tiles.size();
+        final int count = mTiles.size();
         dest.writeInt(count);
 
         for (int n = 0; n < count; n++) {
-            Tile tile = tiles.get(n);
+            Tile tile = mTiles.get(n);
             tile.writeToParcel(dest, flags);
         }
     }
@@ -124,7 +182,7 @@
 
         for (int n = 0; n < count; n++) {
             Tile tile = Tile.CREATOR.createFromParcel(in);
-            tiles.add(tile);
+            mTiles.add(tile);
         }
     }
 
@@ -141,4 +199,13 @@
             return new DashboardCategory[size];
         }
     };
+
+    public static final Comparator<Tile> TILE_COMPARATOR =
+            new Comparator<Tile>() {
+                @Override
+                public int compare(Tile lhs, Tile rhs) {
+                    return rhs.priority - lhs.priority;
+                }
+            };
+
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 038dcf8..e986e0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -253,7 +253,7 @@
         }
         ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
         for (DashboardCategory category : categories) {
-            Collections.sort(category.tiles, TILE_COMPARATOR);
+            category.sortTiles();
         }
         Collections.sort(categories, CATEGORY_COMPARATOR);
         if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
@@ -595,14 +595,6 @@
         return pathSegments.get(0);
     }
 
-    public static final Comparator<Tile> TILE_COMPARATOR =
-            new Comparator<Tile>() {
-        @Override
-        public int compare(Tile lhs, Tile rhs) {
-            return rhs.priority - lhs.priority;
-        }
-    };
-
     private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
             new Comparator<DashboardCategory>() {
         @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index d7eae5f..f099c90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -95,8 +95,9 @@
         mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
 
         assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
-        assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).tiles.size()).isEqualTo(1);
-        assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+        assertThat(
+                mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
+        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
     }
 
     @Test
@@ -114,9 +115,10 @@
         // Added 1 more category to category map.
         assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
         // The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
-        assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).tiles.size()).isEqualTo(1);
+        assertThat(
+                mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
         // Old category still exists.
-        assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
     }
 
     @Test
@@ -136,9 +138,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 200;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -146,9 +148,9 @@
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile3);
-        assertThat(category.tiles.get(1)).isSameAs(tile1);
-        assertThat(category.tiles.get(2)).isSameAs(tile2);
+        assertThat(category.getTile(0)).isSameAs(tile3);
+        assertThat(category.getTile(1)).isSameAs(tile1);
+        assertThat(category.getTile(2)).isSameAs(tile2);
     }
 
     @Test
@@ -169,9 +171,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage1, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -179,9 +181,9 @@
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile2);
-        assertThat(category.tiles.get(1)).isSameAs(tile1);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        assertThat(category.getTile(0)).isSameAs(tile2);
+        assertThat(category.getTile(1)).isSameAs(tile1);
+        assertThat(category.getTile(2)).isSameAs(tile3);
     }
 
     @Test
@@ -202,9 +204,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -212,9 +214,9 @@
                 mCategoryByKeyMap);
 
         // Verify the sorting order is not changed
-        assertThat(category.tiles.get(0)).isSameAs(tile1);
-        assertThat(category.tiles.get(1)).isSameAs(tile2);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        assertThat(category.getTile(0)).isSameAs(tile1);
+        assertThat(category.getTile(1)).isSameAs(tile2);
+        assertThat(category.getTile(2)).isSameAs(tile3);
     }
 
     @Test
@@ -236,10 +238,10 @@
         final Tile tile4 = new Tile();
         tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile4.priority = -1;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
-        category.tiles.add(tile4);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
+        category.addTile(tile4);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -247,10 +249,10 @@
             mCategoryByKeyMap);
 
         // Verify the sorting order is not changed
-        assertThat(category.tiles.get(0)).isSameAs(tile1);
-        assertThat(category.tiles.get(1)).isSameAs(tile2);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
-        assertThat(category.tiles.get(3)).isSameAs(tile4);
+        assertThat(category.getTile(0)).isSameAs(tile1);
+        assertThat(category.getTile(1)).isSameAs(tile2);
+        assertThat(category.getTile(2)).isSameAs(tile3);
+        assertThat(category.getTile(3)).isSameAs(tile4);
     }
 
     @Test
@@ -270,9 +272,9 @@
         final Tile tile3 = new Tile();
         tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
         tile3.priority = 1;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -280,9 +282,9 @@
             mCategoryByKeyMap);
 
         // Verify the sorting order is internal first, follow by package name ordering
-        assertThat(category.tiles.get(0)).isSameAs(tile2);
-        assertThat(category.tiles.get(1)).isSameAs(tile3);
-        assertThat(category.tiles.get(2)).isSameAs(tile1);
+        assertThat(category.getTile(0)).isSameAs(tile2);
+        assertThat(category.getTile(1)).isSameAs(tile3);
+        assertThat(category.getTile(2)).isSameAs(tile1);
     }
 
     @Test
@@ -303,14 +305,14 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
 
-        assertThat(category.tiles.size()).isEqualTo(3);
+        assertThat(category.getTilesCount()).isEqualTo(3);
     }
 
     @Test
@@ -331,13 +333,13 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class1"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
 
-        assertThat(category.tiles.size()).isEqualTo(1);
+        assertThat(category.getTilesCount()).isEqualTo(1);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index dad3a28..a395a4a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -216,7 +216,7 @@
         List<DashboardCategory> categoryList = TileUtils.getCategories(
                 mContext, cache, false /* categoryDefinedInManifest */, testAction,
                 TileUtils.SETTING_PKG);
-        assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory);
+        assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 258c96c..7fb6ede 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -61,6 +61,7 @@
 import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -343,7 +344,8 @@
             }
 
             case Settings.CALL_METHOD_GET_SECURE: {
-                Setting setting = getSecureSetting(name, requestingUserId);
+                Setting setting = getSecureSetting(name, requestingUserId,
+                        /*enableOverride=*/ true);
                 return packageValueForCallResult(setting, isTrackingGeneration(args));
             }
 
@@ -1073,6 +1075,10 @@
     }
 
     private Setting getSecureSetting(String name, int requestingUserId) {
+        return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false);
+    }
+
+    private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
         }
@@ -1102,6 +1108,14 @@
                 return getSsaidSettingLocked(callingPkg, owningUserId);
             }
         }
+        if (enableOverride) {
+            if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
+                final Setting overridden = getLocationProvidersAllowedSetting(owningUserId);
+                if (overridden != null) {
+                    return overridden;
+                }
+            }
+        }
 
         // Not the SSAID; do a straight lookup
         synchronized (mLock) {
@@ -1190,6 +1204,35 @@
         return null;
     }
 
+    private Setting getLocationProvidersAllowedSetting(int owningUserId) {
+        synchronized (mLock) {
+            final Setting setting = getGlobalSetting(
+                    Global.LOCATION_GLOBAL_KILL_SWITCH);
+            if (!"1".equals(setting.getValue())) {
+                return null;
+            }
+            // Global kill-switch is enabled. Return an empty value.
+            final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+                    SETTINGS_TYPE_SECURE, owningUserId);
+            return settingsState.new Setting(
+                    Secure.LOCATION_PROVIDERS_ALLOWED,
+                    "", // value
+                    "", // tag
+                    "", // default value
+                    "", // package name
+                    false, // from system
+                    "0" // id
+            ) {
+                @Override
+                public boolean update(String value, boolean setDefault, String packageName,
+                        String tag, boolean forceNonSystemPackage) {
+                    Slog.wtf(LOG_TAG, "update shoudln't be called on this instance.");
+                    return false;
+                }
+            };
+        }
+    }
+
     private boolean insertSecureSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, boolean forceNotify) {
         if (DEBUG) {
@@ -2780,6 +2823,12 @@
             }
 
             mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
+
+            // When the global kill switch is updated, send the change notification for
+            // the location setting.
+            if (isGlobalSettingsKey(key) && Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)) {
+                notifyLocationChangeForRunningUsers();
+            }
         }
 
         private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
@@ -2799,6 +2848,24 @@
             }
         }
 
+        private void notifyLocationChangeForRunningUsers() {
+            final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true);
+
+            for (int i = 0; i < users.size(); i++) {
+                final int userId = users.get(i).id;
+
+                if (!mUserManager.isUserRunning(UserHandle.of(userId))) {
+                    continue;
+                }
+
+                final int key = makeKey(SETTINGS_TYPE_GLOBAL, userId);
+                final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
+
+                mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
+                        userId, 0, uri).sendToTarget();
+            }
+        }
+
         private boolean isGlobalSettingsKey(int key) {
             return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
         }
@@ -2885,7 +2952,7 @@
                         } catch (SecurityException e) {
                             Slog.w(LOG_TAG, "Failed to notify for " + userId + ": " + uri, e);
                         }
-                        if (DEBUG) {
+                        if (DEBUG || true) {
                             Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri);
                         }
                     } break;
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 12f75bb..0219db3 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -307,8 +307,9 @@
 
     void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
                                                    int removedCount, int addedCount) {
-        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
-                (isFocused() || isSelected() && isShown())) {
+        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
+                && (isFocused() || isSelected() && isShown())) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
             event.setFromIndex(fromIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..51175d1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -387,7 +387,9 @@
             }
             case MotionEvent.ACTION_HOVER_ENTER:
             case MotionEvent.ACTION_HOVER_MOVE: {
-                if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isObservedEventType(
+                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+                        && !mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                     event.setImportantForAccessibility(true);
@@ -400,7 +402,9 @@
                 break;
             }
             case MotionEvent.ACTION_HOVER_EXIT: {
-                if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isObservedEventType(
+                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
+                        && mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                     event.setImportantForAccessibility(true);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index eef43d2..a984680 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,9 +625,7 @@
         @Override
         public void onTaskStackChanged() {
             if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             if (getState() != STATE_NO_PIP) {
                 boolean hasPip = false;
 
@@ -662,9 +660,7 @@
         @Override
         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             StackInfo stackInfo = getPinnedStackInfo();
             if (stackInfo == null) {
                 Log.w(TAG, "Cannot find pinned stack");
@@ -690,9 +686,7 @@
         @Override
         public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
@@ -700,9 +694,7 @@
         @Override
         public void onPinnedStackAnimationEnded() {
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             switch (getState()) {
                 case STATE_PIP_MENU:
                     showPipMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index cf3cae5..2d3080b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -68,7 +68,6 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
 import com.android.systemui.Dependency;
-import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImpl;
@@ -372,7 +371,7 @@
         if (mIam == null) return false;
 
         try {
-            return mIam.isInLockTaskMode();
+            return mIam.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6349275..8e1b104 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -82,6 +82,7 @@
     private static final int MSG_TOGGLE_PANEL                  = 35 << MSG_SHIFT;
     private static final int MSG_SHOW_SHUTDOWN_UI              = 36 << MSG_SHIFT;
     private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR  = 37 << MSG_SHIFT;
+    private static final int MSG_ROTATION_PROPOSAL             = 38 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -142,6 +143,8 @@
         default void handleSystemKey(int arg1) { }
         default void handleShowGlobalActionsMenu() { }
         default void handleShowShutdownUi(boolean isReboot, String reason) { }
+
+        default void onRotationProposal(int rotation) { }
     }
 
     @VisibleForTesting
@@ -458,6 +461,15 @@
         }
     }
 
+    @Override
+    public void onProposedRotationChanged(int rotation) {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
+            mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0,
+                    null).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -654,6 +666,11 @@
                         mCallbacks.get(i).setTopAppHidesStatusBar(msg.arg1 != 0);
                     }
                     break;
+                case MSG_ROTATION_PROPOSAL:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onRotationProposal(msg.arg1);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d41e20..383d327 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -441,7 +441,8 @@
                 .withEndAction(() -> mDialog.dismiss())
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .start();
-        if (mAccessibilityMgr.isEnabled()) {
+        if (mAccessibilityMgr.isObservedEventType(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             event.setPackageName(mContext.getPackageName());
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 7e94d7b..22d922b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -20,9 +20,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
-import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
@@ -49,7 +47,6 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityCache;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -65,6 +62,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -106,7 +104,7 @@
 
     int mFeedbackType;
 
-    Set<String> mPackageNames = new HashSet<>();
+    final Set<String> mPackageNames = new HashSet<>();
 
     boolean mIsDefault;
 
@@ -284,40 +282,98 @@
         return true;
     }
 
-    public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
-        mEventTypes = info.eventTypes;
-        mFeedbackType = info.feedbackType;
-        String[] packageNames = info.packageNames;
-        if (packageNames != null) {
-            mPackageNames.addAll(Arrays.asList(packageNames));
+    boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
+        boolean somethingChanged = false;
+
+        if (mEventTypes != info.eventTypes) {
+            mEventTypes = info.eventTypes;
+            somethingChanged = true;
         }
-        mNotificationTimeout = info.notificationTimeout;
-        mIsDefault = (info.flags & DEFAULT) != 0;
+
+        if (mFeedbackType != info.feedbackType) {
+            mFeedbackType = info.feedbackType;
+            somethingChanged = true;
+        }
+
+        final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]);
+        if (!Arrays.equals(oldPackageNames, info.packageNames)) {
+            mPackageNames.clear();
+            if (info.packageNames != null) {
+                Collections.addAll(mPackageNames, info.packageNames);
+            }
+            somethingChanged = true;
+        }
+
+        if (mNotificationTimeout != info.notificationTimeout) {
+            mNotificationTimeout = info.notificationTimeout;
+            somethingChanged = true;
+        }
+
+        final boolean newIsDefault = (info.flags & DEFAULT) != 0;
+        if (mIsDefault != newIsDefault) {
+            mIsDefault = newIsDefault;
+            somethingChanged = true;
+        }
 
         if (supportsFlagForNotImportantViews(info)) {
-            if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
-                mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            } else {
-                mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            somethingChanged |= updateFetchFlag(info.flags,
+                    AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
+        }
+
+        somethingChanged |= updateFetchFlag(info.flags,
+                AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS);
+
+        final boolean newRequestTouchExplorationMode = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+        if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) {
+            mRequestTouchExplorationMode = newRequestTouchExplorationMode;
+            somethingChanged = true;
+        }
+
+        final boolean newRequestFilterKeyEvents = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+        if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) {
+            mRequestFilterKeyEvents = newRequestFilterKeyEvents;
+            somethingChanged = true;
+        }
+
+        final boolean newRetrieveInteractiveWindows = (info.flags
+                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+        if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) {
+            mRetrieveInteractiveWindows = newRetrieveInteractiveWindows;
+            somethingChanged = true;
+        }
+
+        final boolean newCaptureFingerprintGestures = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
+        if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) {
+            mCaptureFingerprintGestures = newCaptureFingerprintGestures;
+            somethingChanged = true;
+        }
+
+        final boolean newRequestAccessibilityButton = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+        if (mRequestAccessibilityButton != newRequestAccessibilityButton) {
+            mRequestAccessibilityButton = newRequestAccessibilityButton;
+            somethingChanged = true;
+        }
+
+        return somethingChanged;
+    }
+
+    private boolean updateFetchFlag(int allFlags, int flagToUpdate) {
+        if ((allFlags & flagToUpdate) != 0) {
+            if ((mFetchFlags & flagToUpdate) == 0) {
+                mFetchFlags |= flagToUpdate;
+                return true;
+            }
+        } else {
+            if ((mFetchFlags & flagToUpdate) != 0) {
+                mFetchFlags &= ~flagToUpdate;
+                return true;
             }
         }
-
-        if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
-            mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
-        } else {
-            mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
-        }
-
-        mRequestTouchExplorationMode = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-        mRequestFilterKeyEvents = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
-        mRetrieveInteractiveWindows = (info.flags
-                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
-        mCaptureFingerprintGestures = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
-        mRequestAccessibilityButton = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+        return false;
     }
 
     protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -349,14 +405,15 @@
                 // If the XML manifest had data to configure the service its info
                 // should be already set. In such a case update only the dynamically
                 // configurable properties.
+                final boolean serviceInfoChanged;
                 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
                 if (oldInfo != null) {
                     oldInfo.updateDynamicallyConfigurableProperties(info);
-                    setDynamicallyConfigurableProperties(oldInfo);
+                    serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo);
                 } else {
-                    setDynamicallyConfigurableProperties(info);
+                    serviceInfoChanged = setDynamicallyConfigurableProperties(info);
                 }
-                mSystemSupport.onClientChange(true);
+                mSystemSupport.onClientChange(serviceInfoChanged);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3554448..8b5c85a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2400,7 +2400,8 @@
         private void announceNewUserIfNeeded() {
             synchronized (mLock) {
                 UserState userState = getCurrentUserStateLocked();
-                if (userState.isHandlingAccessibilityEvents()) {
+                if (userState.isHandlingAccessibilityEvents()
+                        && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) {
                     UserManager userManager = (UserManager) mContext.getSystemService(
                             Context.USER_SERVICE);
                     String message = mContext.getString(R.string.user_switched,
@@ -3157,13 +3158,21 @@
             if (mWindowsForAccessibilityCallback == null) {
                 return;
             }
+            final int userId;
+            synchronized (mLock) {
+                userId = mCurrentUserId;
+                final UserState userState = getUserStateLocked(userId);
+                if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) {
+                    return;
+                }
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 // Let the client know the windows changed.
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_WINDOWS_CHANGED);
                 event.setEventTime(SystemClock.uptimeMillis());
-                sendAccessibilityEvent(event, mCurrentUserId);
+                sendAccessibilityEvent(event, userId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3368,6 +3377,10 @@
             mUserId = userId;
         }
 
+        public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+            return (mLastSentRelevantEventTypes & type) != 0;
+        }
+
         public int getClientState() {
             int clientState = 0;
             final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3419b80..62017e8 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -791,7 +791,7 @@
      */
     private void sendAccessibilityEvent(int type) {
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-        if (accessibilityManager.isEnabled()) {
+        if (accessibilityManager.isObservedEventType(type)) {
             AccessibilityEvent event = AccessibilityEvent.obtain(type);
             event.setWindowId(mAms.getActiveWindowId());
             accessibilityManager.sendAccessibilityEvent(event);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index e87b4e6..d77e1a2 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -250,7 +250,24 @@
     }
 
     /**
-     * @return whether the requested task is allowed to be launched.
+     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+     * lockTaskMode="always" in the manifest).
+     */
+    boolean isTaskWhitelisted(TaskRecord task) {
+        switch(task.mLockTaskAuth) {
+            case LOCK_TASK_AUTH_WHITELISTED:
+            case LOCK_TASK_AUTH_LAUNCHABLE:
+            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
+                return true;
+            case LOCK_TASK_AUTH_PINNABLE:
+            case LOCK_TASK_AUTH_DONT_LOCK:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * @return whether the requested task is disallowed to be launched.
      */
     boolean isLockTaskModeViolation(TaskRecord task) {
         return isLockTaskModeViolation(task, false);
@@ -258,7 +275,7 @@
 
     /**
      * @param isNewClearTask whether the task would be cleared as part of the operation.
-     * @return whether the requested task is allowed to be launched.
+     * @return whether the requested task is disallowed to be launched.
      */
     boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
         if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
@@ -275,21 +292,18 @@
             // If the task is already at the top and won't be cleared, then allow the operation
             return false;
         }
-        final int lockTaskAuth = task.mLockTaskAuth;
-        switch (lockTaskAuth) {
-            case LOCK_TASK_AUTH_DONT_LOCK:
-                return !mLockTaskModeTasks.isEmpty();
-            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
-            case LOCK_TASK_AUTH_LAUNCHABLE:
-            case LOCK_TASK_AUTH_WHITELISTED:
-                return false;
-            case LOCK_TASK_AUTH_PINNABLE:
-                // Pinnable tasks can't be launched on top of locktask tasks.
-                return !mLockTaskModeTasks.isEmpty();
-            default:
-                Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
-                return true;
+
+        // Allow recents activity if enabled by policy
+        if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
+            return false;
         }
+
+        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+    }
+
+    private boolean isRecentsAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
     }
 
     /**
@@ -491,6 +505,7 @@
         }
 
         if (mLockTaskModeTasks.isEmpty()) {
+            mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
             // Start lock task on the handler thread
             mHandler.post(() -> performStartLockTask(
                     task.intent.getComponent().getPackageName(),
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1a19601..6fb3dbb 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -40,7 +40,7 @@
 /**
  * Activity manager code dealing with processes.
  */
-final class ProcessList {
+public final class ProcessList {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
 
     // The minimum time we allow between crashes, for us to consider this
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index ebcf8c2..abb296e 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -502,6 +502,18 @@
         }
     }
 
+    void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
+        if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
+            return;
+        }
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) {
+                remove(tr);
+            }
+        }
+    }
+
     void removeTasksByPackageName(String packageName, int userId) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final TaskRecord tr = mTasks.get(i);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 90888f0..2c6fe94 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -34,6 +34,7 @@
 import android.os.BatteryManager;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -162,7 +163,10 @@
                 UserHandle.USER_CURRENT);
 
         mSensorListener = new SensorListener();
-        mInjector.registerSensorListener(mContext, mSensorListener);
+
+        if (mInjector.isInteractive(mContext)) {
+            mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler);
+        }
 
         mSettingsObserver = new SettingsObserver(mBgHandler);
         mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
@@ -170,6 +174,8 @@
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_SHUTDOWN);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         mBroadcastReceiver = new Receiver();
         mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
 
@@ -584,6 +590,11 @@
                 if (level != -1 && scale != 0) {
                     batteryLevelChanged(level, scale);
                 }
+            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+                mInjector.unregisterSensorListener(mContext, mSensorListener);
+            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+                mInjector.registerSensorListener(mContext, mSensorListener,
+                        mInjector.getBackgroundHandler());
             }
         }
     }
@@ -591,11 +602,11 @@
     @VisibleForTesting
     static class Injector {
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener) {
+                SensorEventListener sensorListener, Handler handler) {
             SensorManager sensorManager = context.getSystemService(SensorManager.class);
             Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
             sensorManager.registerListener(sensorListener,
-                    lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+                    lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
         }
 
         public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
@@ -675,5 +686,9 @@
         public void cancelIdleJob(Context context) {
             BrightnessIdleJob.cancelJob(context);
         }
+
+        public boolean isInteractive(Context context) {
+            return context.getSystemService(PowerManager.class).isInteractive();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
new file mode 100644
index 0000000..41d9feb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle
+ * notification callbacks. This class implements the IContextHubClient object, and the implemented
+ * APIs must be thread-safe.
+ *
+ * @hide
+ */
+public class ContextHubClientBroker extends IContextHubClient.Stub
+        implements IBinder.DeathRecipient {
+    private static final String TAG = "ContextHubClientBroker";
+
+    /*
+     * The context of the service.
+     */
+    private final Context mContext;
+
+    /*
+     * The proxy to talk to the Context Hub HAL.
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * The manager that registered this client.
+     */
+    private final ContextHubClientManager mClientManager;
+
+    /*
+     * The ID of the hub that this client is attached to.
+     */
+    private final int mAttachedContextHubId;
+
+    /*
+     * The host end point ID of this client.
+     */
+    private final short mHostEndPointId;
+
+    /*
+     * The remote callback interface for this client.
+     */
+    private final IContextHubClientCallback mCallbackInterface;
+
+    /*
+     * false if the connection has been closed by the client, true otherwise.
+     */
+    private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true);
+
+    /* package */ ContextHubClientBroker(
+            Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
+            int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+        mContext = context;
+        mContextHubProxy = contextHubProxy;
+        mClientManager = clientManager;
+        mAttachedContextHubId = contextHubId;
+        mHostEndPointId = hostEndPointId;
+        mCallbackInterface = callback;
+    }
+
+    /**
+     * Attaches a death recipient for this client
+     *
+     * @throws RemoteException if the client has already died
+     */
+    /* package */ void attachDeathRecipient() throws RemoteException {
+        mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+    }
+
+    /**
+     * Sends from this client to a nanoapp.
+     *
+     * @param message the message to send
+     * @return the error code of sending the message
+     */
+    @ContextHubTransaction.Result
+    @Override
+    public int sendMessageToNanoApp(NanoAppMessage message) {
+        ContextHubServiceUtil.checkPermissions(mContext);
+
+        int result;
+        if (mConnectionOpen.get()) {
+            ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
+                    mHostEndPointId, message);
+
+            try {
+                result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
+                        + mAttachedContextHubId + ")", e);
+                result = Result.UNKNOWN_FAILURE;
+            }
+        } else {
+            Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
+            result = Result.UNKNOWN_FAILURE;
+        }
+
+        return ContextHubServiceUtil.toTransactionResult(result);
+    }
+
+    /**
+     * Closes the connection for this client with the service.
+     */
+    @Override
+    public void close() {
+        if (mConnectionOpen.getAndSet(false)) {
+            mClientManager.unregisterClient(mHostEndPointId);
+        }
+    }
+
+    /**
+     * Invoked when the underlying binder of this broker has died at the client process.
+     */
+    public void binderDied() {
+        try {
+            IContextHubClient.Stub.asInterface(this).close();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while closing client on death", e);
+        }
+    }
+
+    /**
+     * @return the ID of the context hub this client is attached to
+     */
+    /* package */ int getAttachedContextHubId() {
+        return mAttachedContextHubId;
+    }
+
+    /**
+     * @return the host endpoint ID of this client
+     */
+    /* package */ short getHostEndPointId() {
+        return mHostEndPointId;
+    }
+
+    /**
+     * Sends a message to the client associated with this object.
+     *
+     * @param message the message that came from a nanoapp
+     */
+    /* package */ void sendMessageToClient(NanoAppMessage message) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onMessageFromNanoApp(message);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = "
+                        + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a nanoapp load event.
+     *
+     * @param nanoAppId the ID of the nanoapp that was loaded.
+     */
+    /* package */ void onNanoAppLoaded(long nanoAppId) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onNanoAppLoaded(nanoAppId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onNanoAppLoaded on client"
+                        + " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a nanoapp unload event.
+     *
+     * @param nanoAppId the ID of the nanoapp that was unloaded.
+     */
+    /* package */ void onNanoAppUnloaded(long nanoAppId) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onNanoAppUnloaded(nanoAppId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onNanoAppUnloaded on client"
+                        + " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a hub reset for this client.
+     */
+    /* package */ void onHubReset() {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onHubReset();
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onHubReset on client" +
+                        " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
new file mode 100644
index 0000000..d58a746
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * A class that manages registration/unregistration of clients and manages messages to/from clients.
+ *
+ * @hide
+ */
+/* package */ class ContextHubClientManager {
+    private static final String TAG = "ContextHubClientManager";
+
+    /*
+     * The maximum host endpoint ID value that a client can be assigned.
+     */
+    private static final int MAX_CLIENT_ID = 0x7fff;
+
+    /*
+     * Local flag to enable debug logging.
+     */
+    private static final boolean DEBUG_LOG_ENABLED = true;
+
+    /*
+     * The context of the service.
+     */
+    private final Context mContext;
+
+    /*
+     * The proxy to talk to the Context Hub.
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients.
+     * A concurrent data structure is used since the registration/unregistration can occur in
+     * multiple threads.
+     */
+    private final ConcurrentHashMap<Short, ContextHubClientBroker> mHostEndPointIdToClientMap =
+            new ConcurrentHashMap<>();
+
+    /*
+     * The next host endpoint ID to start iterating for the next available host endpoint ID.
+     */
+    private int mNextHostEndpointId = 0;
+
+    /* package */ ContextHubClientManager(
+            Context context, IContexthub contextHubProxy) {
+        mContext = context;
+        mContextHubProxy = contextHubProxy;
+    }
+
+    /**
+     * Registers a new client with the service.
+     *
+     * @param clientCallback the callback interface of the client to register
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the client interface
+     *
+     * @throws IllegalStateException if max number of clients have already registered
+     */
+    /* package */ IContextHubClient registerClient(
+            IContextHubClientCallback clientCallback, int contextHubId) {
+        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+
+        try {
+            broker.attachDeathRecipient();
+        } catch (RemoteException e) {
+            // The client process has died, so we close the connection and return null.
+            Log.e(TAG, "Failed to attach death recipient to client");
+            broker.close();
+            return null;
+        }
+
+        Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
+        return IContextHubClient.Stub.asInterface(broker);
+    }
+
+    /**
+     * Handles a message sent from a nanoapp.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp sent the message from
+     * @param message      the message send by a nanoapp
+     */
+    /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) {
+        NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
+
+        if (DEBUG_LOG_ENABLED) {
+            String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast";
+            Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x"
+                    + Long.toHexString(clientMessage.getNanoAppId()));
+        }
+
+        if (clientMessage.isBroadcastMessage()) {
+            broadcastMessage(contextHubId, clientMessage);
+        } else {
+            ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
+            if (proxy != null) {
+                proxy.sendMessageToClient(clientMessage);
+            } else {
+                Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
+                        + message.hostEndPoint + ")");
+            }
+        }
+    }
+
+    /**
+     * Unregisters a client from the service.
+     *
+     * This method should be invoked as a result of a client calling the ContextHubClient.close(),
+     * or if the client process has died.
+     *
+     * @param hostEndPointId the host endpoint ID of the client that has died
+     */
+    /* package */ void unregisterClient(short hostEndPointId) {
+        if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
+            Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
+        } else {
+            Log.e(TAG, "Cannot unregister non-existing client with host endpoint ID "
+                    + hostEndPointId);
+        }
+    }
+
+    /**
+     * Handles a nanoapp load event.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp was loaded.
+     * @param nanoAppId    the ID of the nanoapp that was loaded.
+     */
+    /* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) {
+        forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId));
+    }
+
+    /**
+     * Handles a nanoapp unload event.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp was unloaded.
+     * @param nanoAppId    the ID of the nanoapp that was unloaded.
+     */
+    /* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) {
+        forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId));
+    }
+
+    /**
+     * Handles a hub reset.
+     *
+     * @param contextHubId the ID of the hub that has reset.
+     */
+    /* package */ void onHubReset(int contextHubId) {
+        forEachClientOfHub(contextHubId, client -> client.onHubReset());
+    }
+
+    /**
+     * Creates a new ContextHubClientBroker object for a client and registers it with the client
+     * manager.
+     *
+     * @param clientCallback the callback interface of the client to register
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the ContextHubClientBroker object
+     *
+     * @throws IllegalStateException if max number of clients have already registered
+     */
+    private synchronized ContextHubClientBroker createNewClientBroker(
+            IContextHubClientCallback clientCallback, int contextHubId) {
+        if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
+            throw new IllegalStateException("Could not register client - max limit exceeded");
+        }
+
+        ContextHubClientBroker broker = null;
+        int id = mNextHostEndpointId;
+        for (int i = 0; i <= MAX_CLIENT_ID; i++) {
+            if (!mHostEndPointIdToClientMap.containsKey(id)) {
+                broker = new ContextHubClientBroker(
+                        mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
+                mHostEndPointIdToClientMap.put((short)id, broker);
+                mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+                break;
+            }
+
+            id = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+        }
+
+        return broker;
+    }
+
+    /**
+     * Broadcasts a message from a nanoapp to all clients attached to the associated hub.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp sent the message from
+     * @param message      the message send by a nanoapp
+     */
+    private void broadcastMessage(int contextHubId, NanoAppMessage message) {
+        forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message));
+    }
+
+    /**
+     * Runs a command for each client that is attached to a hub with the given ID.
+     *
+     * @param contextHubId the ID of the hub
+     * @param callback     the command to invoke for the client
+     */
+    private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) {
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            if (broker.getAttachedContextHubId() == contextHubId) {
+                callback.accept(broker);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index da481a8..e08c659 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,12 +16,10 @@
 
 package com.android.server.location;
 
-import android.Manifest;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.HostEndPoint;
 import android.hardware.contexthub.V1_0.HubAppInfo;
 import android.hardware.contexthub.V1_0.IContexthub;
 import android.hardware.contexthub.V1_0.IContexthubCallback;
@@ -29,13 +27,17 @@
 import android.hardware.contexthub.V1_0.TransactionResult;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
+import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.IContextHubService;
 import android.hardware.location.IContextHubTransactionCallback;
 import android.hardware.location.NanoApp;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppFilter;
 import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -49,7 +51,9 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -58,9 +62,6 @@
  */
 public class ContextHubService extends IContextHubService.Stub {
     private static final String TAG = "ContextHubService";
-    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
-    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
-            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
     /*
      * Constants for the type of transaction that is defined by ContextHubService.
@@ -104,6 +105,12 @@
     // The manager for transaction queue
     private final ContextHubTransactionManager mTransactionManager;
 
+    // The manager for sending messages to/from clients
+    private final ContextHubClientManager mClientManager;
+
+    // The default client for old API clients
+    private final Map<Integer, IContextHubClient> mDefaultClientMap;
+
     /**
      * Class extending the callback to register with a Context Hub.
      */
@@ -146,21 +153,34 @@
         mContextHubProxy = getContextHubProxy();
         if (mContextHubProxy == null) {
             mTransactionManager = null;
+            mClientManager = null;
+            mDefaultClientMap = Collections.EMPTY_MAP;
             mContextHubInfo = new ContextHubInfo[0];
             return;
         }
 
-        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+        mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
+        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager);
 
         List<ContextHub> hubList;
         try {
             hubList = mContextHubProxy.getHubs();
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException while getting Context Hub info");
+            Log.e(TAG, "RemoteException while getting Context Hub info", e);
             hubList = Collections.emptyList();
         }
         mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
 
+        HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            int contextHubId = contextHubInfo.getId();
+
+            IContextHubClient client = mClientManager.registerClient(
+                    createDefaultClientCallback(contextHubId), contextHubId);
+            defaultClientMap.put(contextHubId, client);
+        }
+        mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
+
         for (ContextHubInfo contextHubInfo : mContextHubInfo) {
             int contextHubId = contextHubInfo.getId();
             try {
@@ -168,7 +188,7 @@
                         contextHubId, new ContextHubServiceCallback(contextHubId));
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
-                        + contextHubId + ")");
+                        + contextHubId + ")", e);
             }
         }
 
@@ -185,6 +205,53 @@
     }
 
     /**
+     * Creates a default client callback for old API clients.
+     *
+     * @param contextHubId the ID of the hub to attach this client to
+     * @return the internal callback interface
+     */
+    private IContextHubClientCallback createDefaultClientCallback(int contextHubId) {
+        return new IContextHubClientCallback.Stub() {
+            @Override
+            public void onMessageFromNanoApp(NanoAppMessage message) {
+                int nanoAppInstanceId =
+                        mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ?
+                        mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1;
+
+                onMessageReceiptOldApi(
+                        message.getMessageType(), contextHubId, nanoAppInstanceId,
+                        message.getMessageBody());
+            }
+
+            @Override
+            public void onHubReset() {
+                byte[] data = {TransactionResult.SUCCESS};
+                onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+            }
+
+            @Override
+            public void onNanoAppAborted(long nanoAppId, int abortCode) {
+            }
+
+            @Override
+            public void onNanoAppLoaded(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppUnloaded(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppEnabled(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppDisabled(long nanoAppId) {
+            }
+        };
+    }
+
+    /**
      * @return the IContexthub proxy interface
      */
     private IContexthub getContextHubProxy() {
@@ -192,7 +259,7 @@
         try {
             proxy = IContexthub.getService(true /* retry */);
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
         } catch (NoSuchElementException e) {
             Log.i(TAG, "Context Hub HAL service not found");
         }
@@ -204,6 +271,7 @@
     public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
         mCallbacksList.register(callback);
+
         Log.d(TAG, "Added callback, total callbacks " +
                 mCallbacksList.getRegisteredCallbackCount());
         return 0;
@@ -292,7 +360,7 @@
             @Override
             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
                 byte[] data = {(byte) result};
-                onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+                onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
             }
         };
     }
@@ -449,41 +517,37 @@
             return -1;
         }
         if (msg.getData() == null) {
-            Log.w(TAG, "ContextHubMessage message body cannot be null");
+            Log.e(TAG, "ContextHubMessage message body cannot be null");
+            return -1;
+        }
+        if (!mDefaultClientMap.containsKey(hubHandle)) {
+            Log.e(TAG, "Hub with ID " + hubHandle + " does not exist");
             return -1;
         }
 
-        int result;
+        boolean success = false;
         if (nanoAppHandle == OS_APP_INSTANCE) {
             if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
-                result = queryNanoAppsInternal(hubHandle);
+                success = (queryNanoAppsInternal(hubHandle) == Result.OK);
             } else {
                 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
-                result = Result.BAD_PARAMS;
             }
         } else {
             NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
             if (info != null) {
-                ContextHubMsg hubMessage = new ContextHubMsg();
-                hubMessage.appName = info.getAppId();
-                hubMessage.msgType = msg.getMsgType();
-                hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
-                ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
+                NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
+                        info.getAppId(), msg.getMsgType(), msg.getData());
 
-                try {
-                    result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to send nanoapp message - RemoteException");
-                    result = Result.UNKNOWN_FAILURE;
-                }
+                IContextHubClient client = mDefaultClientMap.get(hubHandle);
+                success = (client.sendMessageToNanoApp(message) ==
+                        ContextHubTransaction.TRANSACTION_SUCCESS);
             } else {
                 Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
                         + nanoAppHandle + " does not exist.");
-                result = Result.BAD_PARAMS;
             }
         }
 
-        return (result == Result.OK ? 0 : -1);
+        return success ? 0 : -1;
     }
 
     /**
@@ -493,12 +557,7 @@
      * @param message      the message contents
      */
     private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
-        // TODO(b/67734082): Send to new API clients
-        byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
-
-        int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
-                mNanoAppIdToInstanceMap.get(message.appName) : -1;
-        onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+        mClientManager.onMessageFromNanoApp(contextHubId, message);
     }
 
     /**
@@ -534,7 +593,7 @@
         data[0] = (byte) result;
         ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
 
-        onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+        onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
     }
 
     /**
@@ -552,7 +611,7 @@
 
         byte[] data = new byte[1];
         data[0] = (byte) result;
-        onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+        onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
     }
 
     /**
@@ -577,8 +636,7 @@
             mTransactionManager.onHubReset();
             queryNanoAppsInternal(contextHubId);
 
-            byte[] data = {TransactionResult.SUCCESS};
-            onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+            mClientManager.onHubReset(contextHubId);
         } else {
             Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
                     + eventType + ")");
@@ -641,6 +699,45 @@
         }
     }
 
+    /**
+     * @param contextHubId the hub ID to validate
+     * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
+     */
+    private boolean isValidContextHubId(int contextHubId) {
+        for (ContextHubInfo hubInfo : mContextHubInfo) {
+            if (hubInfo.getId() == contextHubId) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates and registers a client at the service for the specified Context Hub.
+     *
+     * @param clientCallback the client interface to register with the service
+     * @param contextHubId   the ID of the hub this client is attached to
+     * @return the generated client interface, null if registration was unsuccessful
+     *
+     * @throws IllegalArgumentException if contextHubId is not a valid ID
+     * @throws IllegalStateException if max number of clients have already registered
+     * @throws NullPointerException if clientCallback is null
+     */
+    @Override
+    public IContextHubClient createClient(
+            IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException {
+        checkPermissions();
+        if (!isValidContextHubId(contextHubId)) {
+            throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+        }
+        if (clientCallback == null) {
+            throw new NullPointerException("Cannot register client with null callback");
+        }
+
+        return mClientManager.registerClient(clientCallback, contextHubId);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -664,10 +761,10 @@
     }
 
     private void checkPermissions() {
-        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+        ContextHubServiceUtil.checkPermissions(mContext);
     }
 
-    private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+    private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) {
         if (data == null) {
             return -1;
         }
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index ddbaf86..6faeb72 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -16,11 +16,15 @@
 
 package com.android.server.location;
 
+import android.Manifest;
+import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.HostEndPoint;
 import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.Result;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
@@ -34,6 +38,9 @@
  */
 /* package */ class ContextHubServiceUtil {
     private static final String TAG = "ContextHubServiceUtil";
+    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
     /**
      * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
@@ -165,4 +172,40 @@
                 message.appName, message.msgType, messageArray,
                 message.hostEndPoint == HostEndPoint.BROADCAST);
     }
+
+    /**
+     * Checks for location hardware permissions.
+     *
+     * @param context the context of the service
+     */
+    /* package */
+    static void checkPermissions(Context context) {
+        context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+    }
+
+    /**
+     * Helper function to convert from the HAL Result enum error code to the
+     * ContextHubTransaction.Result type.
+     *
+     * @param halResult the Result enum error code
+     * @return the ContextHubTransaction.Result equivalent
+     */
+    @ContextHubTransaction.Result
+    /* package */
+    static int toTransactionResult(int halResult) {
+        switch (halResult) {
+            case Result.OK:
+                return ContextHubTransaction.TRANSACTION_SUCCESS;
+            case Result.BAD_PARAMS:
+                return ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS;
+            case Result.NOT_INIT:
+                return ContextHubTransaction.TRANSACTION_FAILED_UNINITIALIZED;
+            case Result.TRANSACTION_PENDING:
+                return ContextHubTransaction.TRANSACTION_FAILED_PENDING;
+            case Result.TRANSACTION_FAILED:
+            case Result.UNKNOWN_FAILURE:
+            default: /* fall through */
+                return ContextHubTransaction.TRANSACTION_FAILED_UNKNOWN;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 898b76c..47d9d56 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -58,6 +58,11 @@
     private final IContexthub mContextHubProxy;
 
     /*
+     * The manager for all clients for the service.
+     */
+    private final ContextHubClientManager mClientManager;
+
+    /*
      * A queue containing the current transactions
      */
     private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
@@ -73,8 +78,10 @@
     private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
     private ScheduledFuture<?> mTimeoutFuture = null;
 
-    /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+    /* package */ ContextHubTransactionManager(
+            IContexthub contextHubProxy, ContextHubClientManager clientManager) {
         mContextHubProxy = contextHubProxy;
+        mClientManager = clientManager;
     }
 
     /**
@@ -113,6 +120,9 @@
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
+                    if (result == Result.OK) {
+                        mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId());
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while calling client onTransactionComplete");
                 }
@@ -153,6 +163,9 @@
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
+                    if (result == Result.OK) {
+                        mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId);
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while calling client onTransactionComplete");
                 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
new file mode 100644
index 0000000..2bd9cab
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.net;
+
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
+
+import android.app.ActivityManager;
+import android.net.NetworkPolicyManager;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
+import com.android.server.am.ProcessList;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+public class NetworkPolicyLogger {
+    static final String TAG = "NetworkPolicy";
+
+    static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
+    static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int MAX_LOG_SIZE =
+            ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
+    private static final int MAX_NETWORK_BLOCKED_LOG_SIZE =
+            ActivityManager.isLowRamDeviceStatic() ? 50 : 100;
+
+    private static final int EVENT_TYPE_GENERIC = 0;
+    private static final int EVENT_NETWORK_BLOCKED = 1;
+    private static final int EVENT_UID_STATE_CHANGED = 2;
+    private static final int EVENT_POLICIES_CHANGED = 3;
+    private static final int EVENT_METEREDNESS_CHANGED = 4;
+    private static final int EVENT_USER_STATE_REMOVED = 5;
+    private static final int EVENT_RESTRICT_BG_CHANGED = 6;
+    private static final int EVENT_DEVICE_IDLE_MODE_ENABLED = 7;
+    private static final int EVENT_APP_IDLE_STATE_CHANGED = 8;
+    private static final int EVENT_PAROLE_STATE_CHANGED = 9;
+    private static final int EVENT_TEMP_POWER_SAVE_WL_CHANGED = 10;
+    private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
+    private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
+
+    static final int NTWK_BLOCKED_POWER = 0;
+    static final int NTWK_ALLOWED_NON_METERED = 1;
+    static final int NTWK_BLOCKED_BLACKLIST = 2;
+    static final int NTWK_ALLOWED_WHITELIST = 3;
+    static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
+    static final int NTWK_BLOCKED_BG_RESTRICT = 5;
+    static final int NTWK_ALLOWED_DEFAULT = 6;
+
+    private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
+    private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
+    private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE);
+
+    private final Object mLock = new Object();
+
+    void networkBlocked(int uid, int reason) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, uid + " is " + getBlockedReason(reason));
+            mNetworkBlockedBuffer.networkBlocked(uid, reason);
+        }
+    }
+
+    void uidStateChanged(int uid, int procState, long procStateSeq) {
+        synchronized (mLock) {
+            if (LOGV) Slog.v(TAG,
+                    uid + " state changed to " + procState + " with seq=" + procStateSeq);
+            mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq);
+        }
+    }
+
+    void event(String msg) {
+        synchronized (mLock) {
+            if (LOGV) Slog.v(TAG, msg);
+            mEventsBuffer.event(msg);
+        }
+    }
+
+    void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) {
+        synchronized (mLock) {
+            if (LOGV) Slog.v(TAG, getPolicyChangedLog(uid, oldPolicy, newPolicy));
+            mEventsBuffer.uidPolicyChanged(uid, oldPolicy, newPolicy);
+        }
+    }
+
+    void meterednessChanged(int netId, boolean newMetered) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getMeterednessChangedLog(netId, newMetered));
+            mEventsBuffer.meterednessChanged(netId, newMetered);
+        }
+    }
+
+    void removingUserState(int userId) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getUserRemovedLog(userId));
+            mEventsBuffer.userRemoved(userId);
+        }
+    }
+
+    void restrictBackgroundChanged(boolean oldValue, boolean newValue) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG,
+                    getRestrictBackgroundChangedLog(oldValue, newValue));
+            mEventsBuffer.restrictBackgroundChanged(oldValue, newValue);
+        }
+    }
+
+    void deviceIdleModeEnabled(boolean enabled) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getDeviceIdleModeEnabled(enabled));
+            mEventsBuffer.deviceIdleModeEnabled(enabled);
+        }
+    }
+
+    void appIdleStateChanged(int uid, boolean idle) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getAppIdleChangedLog(uid, idle));
+            mEventsBuffer.appIdleStateChanged(uid, idle);
+        }
+    }
+
+    void paroleStateChanged(boolean paroleOn) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
+            mEventsBuffer.paroleStateChanged(paroleOn);
+        }
+    }
+
+    void tempPowerSaveWlChanged(int appId, boolean added) {
+        synchronized (mLock) {
+            if (LOGV) Slog.v(TAG, getTempPowerSaveWlChangedLog(appId, added));
+            mEventsBuffer.tempPowerSaveWlChanged(appId, added);
+        }
+    }
+
+    void uidFirewallRuleChanged(int chain, int uid, int rule) {
+        synchronized (mLock) {
+            if (LOGV) Slog.v(TAG, getUidFirewallRuleChangedLog(chain, uid, rule));
+            mEventsBuffer.uidFirewallRuleChanged(chain, uid, rule);
+        }
+    }
+
+    void firewallChainEnabled(int chain, boolean enabled) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getFirewallChainEnabledLog(chain, enabled));
+            mEventsBuffer.firewallChainEnabled(chain, enabled);
+        }
+    }
+
+    void firewallRulesChanged(int chain, int[] uids, int[] rules) {
+        synchronized (mLock) {
+            final String log = "Firewall rules changed for " + getFirewallChainName(chain)
+                    + "; uids=" + Arrays.toString(uids) + "; rules=" + Arrays.toString(rules);
+            if (LOGD) Slog.d(TAG, log);
+            mEventsBuffer.event(log);
+        }
+    }
+
+    void dumpLogs(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.println();
+            pw.println("mEventLogs (most recent first):");
+            pw.increaseIndent();
+            mEventsBuffer.reverseDump(pw);
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("mNetworkBlockedLogs (most recent first):");
+            pw.increaseIndent();
+            mNetworkBlockedBuffer.reverseDump(pw);
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("mUidStateChangeLogs (most recent first):");
+            pw.increaseIndent();
+            mUidStateChangeBuffer.reverseDump(pw);
+            pw.decreaseIndent();
+        }
+    }
+
+    private static String getBlockedReason(int reason) {
+        switch (reason) {
+            case NTWK_BLOCKED_POWER:
+                return "blocked by power restrictions";
+            case NTWK_ALLOWED_NON_METERED:
+                return "allowed on unmetered network";
+            case NTWK_BLOCKED_BLACKLIST:
+                return "blacklisted on metered network";
+            case NTWK_ALLOWED_WHITELIST:
+                return "whitelisted on metered network";
+            case NTWK_ALLOWED_TMP_WHITELIST:
+                return "temporary whitelisted on metered network";
+            case NTWK_BLOCKED_BG_RESTRICT:
+                return "blocked when background is restricted";
+            case NTWK_ALLOWED_DEFAULT:
+                return "allowed by default";
+            default:
+                return String.valueOf(reason);
+        }
+    }
+
+    private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) {
+        return "Policy for " + uid + " changed from "
+                + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to "
+                + NetworkPolicyManager.uidPoliciesToString(newPolicy);
+    }
+
+    private static String getMeterednessChangedLog(int netId, boolean newMetered) {
+        return "Meteredness of netId=" + netId + " changed to " + newMetered;
+    }
+
+    private static String getUserRemovedLog(int userId) {
+        return "Remove state for u" + userId;
+    }
+
+    private static String getRestrictBackgroundChangedLog(boolean oldValue, boolean newValue) {
+        return "Changed restrictBackground: " + oldValue + "->" + newValue;
+    }
+
+    private static String getDeviceIdleModeEnabled(boolean enabled) {
+        return "DeviceIdleMode enabled: " + enabled;
+    }
+
+    private static String getAppIdleChangedLog(int uid, boolean idle) {
+        return "App idle state of uid " + uid + ": " + idle;
+    }
+
+    private static String getParoleStateChanged(boolean paroleOn) {
+        return "Parole state: " + paroleOn;
+    }
+
+    private static String getTempPowerSaveWlChangedLog(int appId, boolean added) {
+        return "temp-power-save whitelist for " + appId + " changed to: " + added;
+    }
+
+    private static String getUidFirewallRuleChangedLog(int chain, int uid, int rule) {
+        return String.format("Firewall rule changed: %d-%s-%s",
+                uid, getFirewallChainName(chain), getFirewallRuleName(rule));
+    }
+
+    private static String getFirewallChainEnabledLog(int chain, boolean enabled) {
+        return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled;
+    }
+
+    private static String getFirewallChainName(int chain) {
+        switch (chain) {
+            case FIREWALL_CHAIN_DOZABLE:
+                return FIREWALL_CHAIN_NAME_DOZABLE;
+            case FIREWALL_CHAIN_STANDBY:
+                return FIREWALL_CHAIN_NAME_STANDBY;
+            case FIREWALL_CHAIN_POWERSAVE:
+                return FIREWALL_CHAIN_NAME_POWERSAVE;
+            default:
+                return String.valueOf(chain);
+        }
+    }
+
+    private static String getFirewallRuleName(int rule) {
+        switch (rule) {
+            case FIREWALL_RULE_DEFAULT:
+                return "default";
+            case FIREWALL_RULE_ALLOW:
+                return "allow";
+            case FIREWALL_RULE_DENY:
+                return "deny";
+            default:
+                return String.valueOf(rule);
+        }
+    }
+
+    private final static class LogBuffer extends RingBuffer<Data> {
+        private static final SimpleDateFormat sFormatter
+                = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSS");
+        private static final Date sDate = new Date();
+
+        public LogBuffer(int capacity) {
+            super(Data.class, capacity);
+        }
+
+        public void uidStateChanged(int uid, int procState, long procStateSeq) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_UID_STATE_CHANGED;
+            data.ifield1 = uid;
+            data.ifield2 = procState;
+            data.lfield1 = procStateSeq;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void event(String msg) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_TYPE_GENERIC;
+            data.sfield1 = msg;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void networkBlocked(int uid, int reason) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_NETWORK_BLOCKED;
+            data.ifield1 = uid;
+            data.ifield2 = reason;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_POLICIES_CHANGED;
+            data.ifield1 = uid;
+            data.ifield2 = oldPolicy;
+            data.ifield3 = newPolicy;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void meterednessChanged(int netId, boolean newMetered) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_METEREDNESS_CHANGED;
+            data.ifield1 = netId;
+            data.bfield1 = newMetered;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void userRemoved(int userId) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_USER_STATE_REMOVED;
+            data.ifield1 = userId;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void restrictBackgroundChanged(boolean oldValue, boolean newValue) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_RESTRICT_BG_CHANGED;
+            data.bfield1 = oldValue;
+            data.bfield2 = newValue;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void deviceIdleModeEnabled(boolean enabled) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_DEVICE_IDLE_MODE_ENABLED;
+            data.bfield1 = enabled;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void appIdleStateChanged(int uid, boolean idle) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_APP_IDLE_STATE_CHANGED;
+            data.ifield1 = uid;
+            data.bfield1 = idle;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void paroleStateChanged(boolean paroleOn) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_PAROLE_STATE_CHANGED;
+            data.bfield1 = paroleOn;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void tempPowerSaveWlChanged(int appId, boolean added) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_TEMP_POWER_SAVE_WL_CHANGED;
+            data.ifield1 = appId;
+            data.bfield1 = added;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void uidFirewallRuleChanged(int chain, int uid, int rule) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_UID_FIREWALL_RULE_CHANGED;
+            data.ifield1 = chain;
+            data.ifield2 = uid;
+            data.ifield3 = rule;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void firewallChainEnabled(int chain, boolean enabled) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_FIREWALL_CHAIN_ENABLED;
+            data.ifield1 = chain;
+            data.bfield1 = enabled;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void reverseDump(IndentingPrintWriter pw) {
+            final Data[] allData = toArray();
+            for (int i = allData.length - 1; i >= 0; --i) {
+                if (allData[i] == null) {
+                    pw.println("NULL");
+                    continue;
+                }
+                pw.print(formatDate(allData[i].timeStamp));
+                pw.print(" - ");
+                pw.println(getContent(allData[i]));
+            }
+        }
+
+        public String getContent(Data data) {
+            switch (data.type) {
+                case EVENT_TYPE_GENERIC:
+                    return data.sfield1;
+                case EVENT_NETWORK_BLOCKED:
+                    return data.ifield1 + "-" + getBlockedReason(data.ifield2);
+                case EVENT_UID_STATE_CHANGED:
+                    return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2)
+                            + "-" + data.lfield1;
+                case EVENT_POLICIES_CHANGED:
+                    return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3);
+                case EVENT_METEREDNESS_CHANGED:
+                    return getMeterednessChangedLog(data.ifield1, data.bfield1);
+                case EVENT_USER_STATE_REMOVED:
+                    return getUserRemovedLog(data.ifield1);
+                case EVENT_RESTRICT_BG_CHANGED:
+                    return getRestrictBackgroundChangedLog(data.bfield1, data.bfield2);
+                case EVENT_DEVICE_IDLE_MODE_ENABLED:
+                    return getDeviceIdleModeEnabled(data.bfield1);
+                case EVENT_APP_IDLE_STATE_CHANGED:
+                    return getAppIdleChangedLog(data.ifield1, data.bfield1);
+                case EVENT_PAROLE_STATE_CHANGED:
+                    return getParoleStateChanged(data.bfield1);
+                case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
+                    return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1);
+                case EVENT_UID_FIREWALL_RULE_CHANGED:
+                    return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3);
+                case EVENT_FIREWALL_CHAIN_ENABLED:
+                    return getFirewallChainEnabledLog(data.ifield1, data.bfield1);
+                default:
+                    return String.valueOf(data.type);
+            }
+        }
+
+        private String formatDate(long millis) {
+            sDate.setTime(millis);
+            return sFormatter.format(sDate);
+        }
+    }
+
+    public final static class Data {
+        int type;
+        long timeStamp;
+
+        int ifield1;
+        int ifield2;
+        int ifield3;
+        long lfield1;
+        boolean bfield1;
+        boolean bfield2;
+        String sfield1;
+
+        public void reset(){
+            sfield1 = null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 3fa3cd4..fdfe241 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -82,6 +82,13 @@
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BLACKLIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -248,9 +255,9 @@
  * </ul>
  */
 public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
-    static final String TAG = "NetworkPolicy";
-    private static final boolean LOGD = false;
-    private static final boolean LOGV = false;
+    static final String TAG = NetworkPolicyLogger.TAG;
+    private static final boolean LOGD = NetworkPolicyLogger.LOGD;
+    private static final boolean LOGV = NetworkPolicyLogger.LOGV;
 
     private static final int VERSION_INIT = 1;
     private static final int VERSION_ADDED_SNOOZE = 2;
@@ -265,13 +272,6 @@
     private static final int VERSION_ADDED_CYCLE = 11;
     private static final int VERSION_LATEST = VERSION_ADDED_CYCLE;
 
-    /**
-     * Max items written to {@link #ProcStateSeqHistory}.
-     */
-    @VisibleForTesting
-    public static final int MAX_PROC_STATE_SEQ_HISTORY =
-            ActivityManager.isLowRamDeviceStatic() ? 50 : 200;
-
     @VisibleForTesting
     public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
     @VisibleForTesting
@@ -471,13 +471,7 @@
 
     private ActivityManagerInternal mActivityManagerInternal;
 
-    /**
-     * This is used for debugging purposes. Whenever the IUidObserver.onUidStateChanged is called,
-     * the uid and procStateSeq will be written to this and will be printed as part of dump.
-     */
-    @VisibleForTesting
-    public ProcStateSeqHistory mObservedHistory
-            = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY);
+    private final NetworkPolicyLogger mLogger = new NetworkPolicyLogger();
 
     // TODO: keep whitelist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
@@ -966,6 +960,7 @@
                         .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
 
                 if ((oldMetered != newMetered) || mNetworkMetered.indexOfKey(network.netId) < 0) {
+                    mLogger.meterednessChanged(network.netId, newMetered);
                     mNetworkMetered.put(network.netId, newMetered);
                     updateNetworkRulesNL();
                 }
@@ -2148,6 +2143,7 @@
                 final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
                 if (oldPolicy != policy) {
                     setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                    mLogger.uidPolicyChanged(uid, oldPolicy, policy);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -2168,6 +2164,7 @@
             policy |= oldPolicy;
             if (oldPolicy != policy) {
                 setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
             }
         }
     }
@@ -2185,6 +2182,7 @@
             policy = oldPolicy & ~policy;
             if (oldPolicy != policy) {
                 setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
             }
         }
     }
@@ -2264,7 +2262,7 @@
      */
     boolean removeUserStateUL(int userId, boolean writePolicy) {
 
-        if (LOGV) Slog.v(TAG, "removeUserStateUL()");
+        mLogger.removingUserState(userId);
         boolean changed = false;
 
         // Remove entries from revoked default restricted background UID whitelist
@@ -2429,7 +2427,6 @@
     @Override
     public void onTetheringChanged(String iface, boolean tethering) {
         // No need to enforce permission because setRestrictBackground() will do it.
-        if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")");
         synchronized (mUidRulesFirstLock) {
             if (mRestrictBackground && tethering) {
                 Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
@@ -2486,6 +2483,7 @@
             }
 
             sendRestrictBackgroundChangedMsg();
+            mLogger.restrictBackgroundChanged(oldRestrictBackground, mRestrictBackground);
 
             if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) {
                 mRestrictBackgroundChangedInBsm = true;
@@ -2551,6 +2549,7 @@
                     return;
                 }
                 mDeviceIdleMode = enabled;
+                mLogger.deviceIdleModeEnabled(enabled);
                 if (mSystemReady) {
                     // Device idle change means we need to rebuild rules for all
                     // known apps, so do a global refresh.
@@ -2964,10 +2963,7 @@
                 }
                 fout.decreaseIndent();
 
-                fout.println("Observed uid state changes:");
-                fout.increaseIndent();
-                mObservedHistory.dumpUL(fout);
-                fout.decreaseIndent();
+                mLogger.dumpLogs(fout);
             }
         }
     }
@@ -3750,8 +3746,8 @@
             try {
                 final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-                if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
                 synchronized (mUidRulesFirstLock) {
+                    mLogger.appIdleStateChanged(uid, idle);
                     updateRuleForAppIdleUL(uid);
                     updateRulesForPowerRestrictionsUL(uid);
                 }
@@ -3762,6 +3758,7 @@
         @Override
         public void onParoleStateChanged(boolean isParoleOn) {
             synchronized (mUidRulesFirstLock) {
+                mLogger.paroleStateChanged(isParoleOn);
                 updateRulesForAppIdleParoleUL();
             }
         }
@@ -3947,7 +3944,7 @@
             synchronized (mUidRulesFirstLock) {
                 // We received a uid state change callback, add it to the history so that it
                 // will be useful for debugging.
-                mObservedHistory.addProcStateSeqUL(uid, procStateSeq);
+                mLogger.uidStateChanged(uid, procState, procStateSeq);
                 // Now update the network policy rules as per the updated uid state.
                 updateUidStateUL(uid, procState);
                 // Updating the network rules is done, so notify AMS about this.
@@ -4081,6 +4078,7 @@
                 rules[index] = uidRules.valueAt(index);
             }
             mNetworkManager.setFirewallUidRules(chain, uids, rules);
+            mLogger.firewallRulesChanged(chain, uids, rules);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting firewall uid rules", e);
         } catch (RemoteException e) {
@@ -4107,6 +4105,7 @@
 
             try {
                 mNetworkManager.setFirewallUidRule(chain, uid, rule);
+                mLogger.uidFirewallRuleChanged(chain, uid, rule);
             } catch (IllegalStateException e) {
                 Log.wtf(TAG, "problem setting firewall uid rules", e);
             } catch (RemoteException e) {
@@ -4129,6 +4128,7 @@
         mFirewallChainStates.put(chain, enable);
         try {
             mNetworkManager.setFirewallChainEnabled(chain, enable);
+            mLogger.firewallChainEnabled(chain, enable);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem enable firewall chain", e);
         } catch (RemoteException e) {
@@ -4305,30 +4305,30 @@
             isBackgroundRestricted = mRestrictBackground;
         }
         if (hasRule(uidRules, RULE_REJECT_ALL)) {
-            if (LOGV) logUidStatus(uid, "blocked by power restrictions");
+            mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
             return true;
         }
         if (!isNetworkMetered) {
-            if (LOGV) logUidStatus(uid, "allowed on unmetered network");
+            mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
             return false;
         }
         if (hasRule(uidRules, RULE_REJECT_METERED)) {
-            if (LOGV) logUidStatus(uid, "blacklisted on metered network");
+            mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
             return true;
         }
         if (hasRule(uidRules, RULE_ALLOW_METERED)) {
-            if (LOGV) logUidStatus(uid, "whitelisted on metered network");
+            mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
             return false;
         }
         if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
-            if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network");
+            mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
             return false;
         }
         if (isBackgroundRestricted) {
-            if (LOGV) logUidStatus(uid, "blocked when background is restricted");
+            mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
             return true;
         }
-        if (LOGV) logUidStatus(uid, "allowed by default");
+        mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
         return false;
     }
 
@@ -4379,6 +4379,7 @@
         @Override
         public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
             synchronized (mUidRulesFirstLock) {
+                mLogger.tempPowerSaveWlChanged(appId, added);
                 if (added) {
                     mPowerSaveTempWhitelistAppIds.put(appId, true);
                 } else {
@@ -4393,80 +4394,6 @@
         return (uidRules & rule) != 0;
     }
 
-    private static void logUidStatus(int uid, String descr) {
-        Slog.d(TAG, String.format("uid %d is %s", uid, descr));
-    }
-
-    /**
-     * This class is used for storing and dumping the last {@link #MAX_PROC_STATE_SEQ_HISTORY}
-     * (uid, procStateSeq) pairs.
-     */
-    @VisibleForTesting
-    public static final class ProcStateSeqHistory {
-        private static final int INVALID_UID = -1;
-
-        /**
-         * Denotes maximum number of items this history can hold.
-         */
-        private final int mMaxCapacity;
-        /**
-         * Used for storing the uid information.
-         */
-        private final int[] mUids;
-        /**
-         * Used for storing the sequence numbers associated with {@link #mUids}.
-         */
-        private final long[] mProcStateSeqs;
-        /**
-         * Points to the next available slot for writing (uid, procStateSeq) pair.
-         */
-        private int mHistoryNext;
-
-        public ProcStateSeqHistory(int maxCapacity) {
-            mMaxCapacity = maxCapacity;
-            mUids = new int[mMaxCapacity];
-            Arrays.fill(mUids, INVALID_UID);
-            mProcStateSeqs = new long[mMaxCapacity];
-        }
-
-        @GuardedBy("mUidRulesFirstLock")
-        public void addProcStateSeqUL(int uid, long procStateSeq) {
-            mUids[mHistoryNext] = uid;
-            mProcStateSeqs[mHistoryNext] = procStateSeq;
-            mHistoryNext = increaseNext(mHistoryNext, 1);
-        }
-
-        @GuardedBy("mUidRulesFirstLock")
-        public void dumpUL(IndentingPrintWriter fout) {
-            if (mUids[0] == INVALID_UID) {
-                fout.println("NONE");
-                return;
-            }
-            int index = mHistoryNext;
-            do {
-                index = increaseNext(index, -1);
-                if (mUids[index] == INVALID_UID) {
-                    break;
-                }
-                fout.println(getString(mUids[index], mProcStateSeqs[index]));
-            } while (index != mHistoryNext);
-        }
-
-        public static String getString(int uid, long procStateSeq) {
-            return "UID=" + uid + " Seq=" + procStateSeq;
-        }
-
-        private int increaseNext(int next, int increment) {
-            next += increment;
-            if (next >= mMaxCapacity) {
-                next = 0;
-            } else if (next < 0) {
-                next = mMaxCapacity - 1;
-            }
-            return next;
-        }
-    }
-
     private class NotificationId {
         private final String mTag;
         private final int mId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cf01400..bec6fc2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4743,7 +4743,8 @@
     }
 
     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
-        if (!mAccessibilityManager.isEnabled()) {
+        if (!mAccessibilityManager.isObservedEventType(
+                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b07fe98..3792bc6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -90,6 +90,13 @@
 
     boolean showShutdownUi(boolean isReboot, String requestString);
 
+    /**
+     * Show a rotation suggestion that a user may approve to rotate the screen.
+     *
+     * @param rotation rotation suggestion
+     */
+    void onProposedRotationChanged(int rotation);
+
     public interface GlobalActionsListener {
         /**
          * Called when sysui starts and connects its status bar, or when the status bar binder
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c78a340..c7c03b4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -406,6 +406,15 @@
             }
             return false;
         }
+
+        @Override
+        public void onProposedRotationChanged(int rotation) {
+            if (mBar != null){
+                try {
+                    mBar.onProposedRotationChanged(rotation);
+                } catch (RemoteException ex) {}
+            }
+        }
     };
 
     // ================================================================================
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 4a2da37..8b9cf4b 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -94,6 +94,7 @@
     android.hardware.broadcastradio@1.2 \
     android.hardware.contexthub@1.0 \
     android.hardware.gnss@1.0 \
+    android.hardware.gnss@1.1 \
     android.hardware.ir@1.0 \
     android.hardware.light@2.0 \
     android.hardware.power@1.0 \
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index daf3f2f..4cbe64d 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -18,7 +18,7 @@
 
 #define LOG_NDEBUG 0
 
-#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/1.1/IGnss.h>
 
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
@@ -78,6 +78,9 @@
 using android::hardware::hidl_death_recipient;
 using android::hidl::base::V1_0::IBase;
 
+using android::hardware::gnss::V1_0::GnssLocation;
+using android::hardware::gnss::V1_0::GnssLocationFlags;
+
 using android::hardware::gnss::V1_0::IAGnss;
 using android::hardware::gnss::V1_0::IAGnssCallback;
 using android::hardware::gnss::V1_0::IAGnssCallback;
@@ -86,7 +89,6 @@
 using android::hardware::gnss::V1_0::IGnss;
 using android::hardware::gnss::V1_0::IGnssBatching;
 using android::hardware::gnss::V1_0::IGnssBatchingCallback;
-using android::hardware::gnss::V1_0::IGnssCallback;
 using android::hardware::gnss::V1_0::IGnssConfiguration;
 using android::hardware::gnss::V1_0::IGnssDebug;
 using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -100,11 +102,14 @@
 using android::hardware::gnss::V1_0::IGnssXtra;
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
 
+using IGnssV1_1 = android::hardware::gnss::V1_1::IGnss;
+using android::hardware::gnss::V1_1::IGnssCallback;
+
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
     // hidl_death_recipient interface
     virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
-      // TODO(gomo): implement a better death recovery mechanism without
+      // TODO(b/37460011): implement a better death recovery mechanism without
       // crashing system server process as described in go//treble-gnss-death
       LOG_ALWAYS_FATAL("Abort due to IGNSS hidl service failure,"
             " restarting system server");
@@ -113,6 +118,7 @@
 
 sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
 sp<IGnss> gnssHal = nullptr;
+sp<IGnssV1_1> gnssHalV1_1 = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
 sp<IAGnssRil> agnssRilIface = nullptr;
 sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -303,33 +309,33 @@
     return env;
 }
 
-static jobject translateLocation(JNIEnv* env, const hardware::gnss::V1_0::GnssLocation& location) {
+static jobject translateLocation(JNIEnv* env, const GnssLocation& location) {
     JavaObject object(env, "android/location/Location", "gps");
 
     uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) {
+    if (flags & GnssLocationFlags::HAS_LAT_LONG) {
         SET(Latitude, location.latitudeDegrees);
         SET(Longitude, location.longitudeDegrees);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) {
+    if (flags & GnssLocationFlags::HAS_ALTITUDE) {
         SET(Altitude, location.altitudeMeters);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) {
+    if (flags & GnssLocationFlags::HAS_SPEED) {
         SET(Speed, location.speedMetersPerSec);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) {
+    if (flags & GnssLocationFlags::HAS_BEARING) {
         SET(Bearing, location.bearingDegrees);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
+    if (flags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
         SET(Accuracy, location.horizontalAccuracyMeters);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+    if (flags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
         SET(VerticalAccuracyMeters, location.verticalAccuracyMeters);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) {
+    if (flags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
         SET(SpeedAccuracyMetersPerSecond, location.speedAccuracyMetersPerSecond);
     }
-    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) {
+    if (flags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
         SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
     }
     SET(Time, location.timestamp);
@@ -341,8 +347,7 @@
  * GnssCallback class implements the callback methods for IGnss interface.
  */
 struct GnssCallback : public IGnssCallback {
-    Return<void> gnssLocationCb(
-          const android::hardware::gnss::V1_0::GnssLocation& location) override;
+    Return<void> gnssLocationCb(const GnssLocation& location) override;
     Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
     Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
     Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
@@ -352,6 +357,9 @@
     Return<void> gnssRequestTimeCb() override;
     Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
 
+    // New in 1.1
+    Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
+
     static GnssSvInfo sGnssSvList[static_cast<uint32_t>(
             android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
     static size_t sGnssSvListSize;
@@ -360,19 +368,30 @@
     static size_t sNmeaStringLength;
 };
 
+Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
+    ALOGD("%s: name=%s\n", __func__, name.c_str());
+
+    // TODO(b/38003769): build Java code to connect to below code
+    /*
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareName, name);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    */
+    return Void();
+}
+
 IGnssCallback::GnssSvInfo GnssCallback::sGnssSvList[static_cast<uint32_t>(
         android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
 const char* GnssCallback::sNmeaString = nullptr;
 size_t GnssCallback::sNmeaStringLength = 0;
 size_t GnssCallback::sGnssSvListSize = 0;
 
-Return<void> GnssCallback::gnssLocationCb(
-        const ::android::hardware::gnss::V1_0::GnssLocation& location) {
+Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) {
     JNIEnv* env = getJniEnv();
 
     jobject jLocation = translateLocation(env, location);
     bool hasLatLong = (static_cast<uint32_t>(location.gnssLocationFlags) &
-            hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) != 0;
+            GnssLocationFlags::HAS_LAT_LONG) != 0;
 
     env->CallVoidMethod(mCallbacksObj,
                         method_reportLocation,
@@ -484,12 +503,12 @@
     // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
     Return<void> gnssGeofenceTransitionCb(
             int32_t geofenceId,
-            const android::hardware::gnss::V1_0::GnssLocation& location,
+            const GnssLocation& location,
             GeofenceTransition transition,
             hardware::gnss::V1_0::GnssUtcTime timestamp) override;
     Return<void> gnssGeofenceStatusCb(
             GeofenceAvailability status,
-            const android::hardware::gnss::V1_0::GnssLocation& location) override;
+            const GnssLocation& location) override;
     Return<void> gnssGeofenceAddCb(int32_t geofenceId,
                                    GeofenceStatus status) override;
     Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
@@ -502,7 +521,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
         int32_t geofenceId,
-        const android::hardware::gnss::V1_0::GnssLocation& location,
+        const GnssLocation& location,
         GeofenceTransition transition,
         hardware::gnss::V1_0::GnssUtcTime timestamp) {
     JNIEnv* env = getJniEnv();
@@ -522,7 +541,7 @@
 
 Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
         GeofenceAvailability status,
-        const android::hardware::gnss::V1_0::GnssLocation& location) {
+        const GnssLocation& location) {
     JNIEnv* env = getJniEnv();
 
     jobject jLocation = translateLocation(env, location);
@@ -976,12 +995,12 @@
     * follow.
     */
     Return<void> gnssLocationBatchCb(
-        const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations)
+        const ::android::hardware::hidl_vec<GnssLocation> & locations)
         override;
 };
 
 Return<void> GnssBatchingCallback::gnssLocationBatchCb(
-        const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) {
+        const ::android::hardware::hidl_vec<GnssLocation> & locations) {
     JNIEnv* env = getJniEnv();
 
     jobjectArray jLocations = env->NewObjectArray(locations.size(),
@@ -1049,8 +1068,13 @@
         LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
     }
 
-    // TODO(b/31632518)
-    gnssHal = IGnss::getService();
+    gnssHal = gnssHalV1_1 = IGnssV1_1::getService();
+
+    if (gnssHal == nullptr) {
+        ALOGD("gnssHal 1.1 was null, trying 1.0");
+        gnssHal = IGnss::getService();
+    }
+
     if (gnssHal != nullptr) {
       gnssHalDeathRecipient = new GnssDeathRecipient();
       hardware::Return<bool> linked = gnssHal->linkToDeath(
@@ -1160,7 +1184,6 @@
     if (!mCallbacksObj)
         mCallbacksObj = env->NewGlobalRef(obj);
 
-    sp<IGnssCallback> gnssCbIface = new GnssCallback();
     /*
      * Fail if the main interface fails to initialize
      */
@@ -1169,7 +1192,14 @@
         return JNI_FALSE;
     }
 
-    auto result = gnssHal->setCallback(gnssCbIface);
+    sp<IGnssCallback> gnssCbIface = new GnssCallback();
+
+    Return<bool> result = false;
+    if (gnssHalV1_1 != nullptr) {
+        result = gnssHalV1_1->setCallback_1_1(gnssCbIface);
+    } else {
+        result = gnssHal->setCallback(gnssCbIface);
+    }
     if (!result.isOk() || !result) {
         ALOGE("SetCallback for Gnss Interface fails\n");
         return JNI_FALSE;
@@ -1182,7 +1212,7 @@
         result = gnssXtraIface->setCallback(gnssXtraCbIface);
         if (!result.isOk() || !result) {
             gnssXtraIface = nullptr;
-            ALOGE("SetCallback for Gnss Xtra Interface fails\n");
+            ALOGI("SetCallback for Gnss Xtra Interface fails\n");
         }
     }
 
@@ -1190,21 +1220,21 @@
     if (agnssIface != nullptr) {
         agnssIface->setCallback(aGnssCbIface);
     } else {
-        ALOGE("Unable to Initialize AGnss interface\n");
+        ALOGI("Unable to Initialize AGnss interface\n");
     }
 
     sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
     if (gnssGeofencingIface != nullptr) {
       gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
     } else {
-        ALOGE("Unable to initialize GNSS Geofencing interface\n");
+        ALOGI("Unable to initialize GNSS Geofencing interface\n");
     }
 
     sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
     if (gnssNiIface != nullptr) {
         gnssNiIface->setCallback(gnssNiCbIface);
     } else {
-        ALOGE("Unable to initialize GNSS NI interface\n");
+        ALOGI("Unable to initialize GNSS NI interface\n");
     }
 
     sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 4078829..f093d57 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -37,7 +37,6 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.Time.TIMEZONE_UTC;
 
-import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
@@ -61,7 +60,6 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -116,12 +114,10 @@
 import android.util.TrustedTime;
 
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -142,12 +138,10 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.PrintWriter;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -191,8 +185,6 @@
     private static final String TEST_IFACE = "test0";
     private static final String TEST_SSID = "AndroidAP";
 
-    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
-
     private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
 
     /**
@@ -1109,14 +1101,6 @@
         final long procStateSeq = 222;
         callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq);
         verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq);
-
-        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        final IndentingPrintWriter writer = new IndentingPrintWriter(
-                new PrintWriter(outputStream), " ");
-        mService.mObservedHistory.dumpUL(writer);
-        writer.flush();
-        assertEquals(ProcStateSeqHistory.getString(UID_A, procStateSeq),
-                outputStream.toString().trim());
     }
 
     private void callOnUidStateChanged(int uid, int procState, long procStateSeq)
@@ -1129,59 +1113,6 @@
         latch.await(2, TimeUnit.SECONDS);
     }
 
-    @Test
-    public void testProcStateHistory() {
-        // Verify dump works correctly with no elements added.
-        verifyProcStateHistoryDump(0);
-
-        // Add items upto half of the max capacity and verify that dump works correctly.
-        verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY / 2);
-
-        // Add items upto the max capacity and verify that dump works correctly.
-        verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY);
-
-        // Add more items than max capacity and verify that dump works correctly.
-        verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY + MAX_PROC_STATE_SEQ_HISTORY / 2);
-
-    }
-
-    private void verifyProcStateHistoryDump(int count) {
-        final ProcStateSeqHistory history = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY);
-        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        final IndentingPrintWriter writer = new IndentingPrintWriter(
-                new PrintWriter(outputStream), " ");
-
-        if (count == 0) {
-            // Verify with no uid info written to history.
-            history.dumpUL(writer);
-            writer.flush();
-            assertEquals("When no uid info is there, dump should contain NONE",
-                    "NONE", outputStream.toString().trim());
-            return;
-        }
-
-        int uid = 111;
-        long procStateSeq = 222;
-        // Add count items and verify dump works correctly.
-        for (int i = 0; i < count; ++i) {
-            uid++;
-            procStateSeq++;
-            history.addProcStateSeqUL(uid, procStateSeq);
-        }
-        history.dumpUL(writer);
-        writer.flush();
-        final String[] uidsDump = outputStream.toString().split(LINE_SEPARATOR);
-        // Dump will have at most MAX_PROC_STATE_SEQ_HISTORY items.
-        final int expectedCount = (count < MAX_PROC_STATE_SEQ_HISTORY)
-                ? count : MAX_PROC_STATE_SEQ_HISTORY;
-        assertEquals(expectedCount, uidsDump.length);
-        for (int i = 0; i < expectedCount; ++i) {
-            assertEquals(ProcStateSeqHistory.getString(uid, procStateSeq), uidsDump[i]);
-            uid--;
-            procStateSeq--;
-        }
-    }
-
     private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay,
             boolean expectValid) {
         config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay);
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 1adfb67..54df744 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,6 +90,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private RecentTasks mRecentTasks;
 
     private LockTaskController mLockTaskController;
     private Context mContext;
@@ -110,9 +111,10 @@
             Looper.prepare();
         }
 
+        mSupervisor.mRecentTasks = mRecentTasks;
+
         mLockTaskController = new LockTaskController(mContext, mSupervisor,
                 new ImmediatelyExecuteHandler());
-
         mLockTaskController.setWindowManager(mWindowManager);
         mLockTaskController.mStatusBarService = mStatusBarService;
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
@@ -601,6 +603,8 @@
                 eq(mContext.getPackageName()));
         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
                 eq(mContext.getPackageName()));
+        // THEN recents should have been notified
+        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
         // THEN the DO/PO should be informed about the operation
         verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
                 TEST_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 6938e0f..6a1d268 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -94,12 +94,29 @@
     }
 
     @Test
-    public void testStartStopTracker() {
+    public void testStartStopTrackerScreenOnOff() {
+        mInjector.mInteractive = false;
         startTracker(mTracker);
-        assertNotNull(mInjector.mSensorListener);
+        assertNull(mInjector.mSensorListener);
         assertNotNull(mInjector.mSettingsObserver);
         assertNotNull(mInjector.mBroadcastReceiver);
         assertTrue(mInjector.mIdleScheduled);
+        Intent onIntent = new Intent();
+        onIntent.setAction(Intent.ACTION_SCREEN_ON);
+        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+                onIntent);
+        assertNotNull(mInjector.mSensorListener);
+
+        Intent offIntent = new Intent();
+        offIntent.setAction(Intent.ACTION_SCREEN_OFF);
+        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+                offIntent);
+        assertNull(mInjector.mSensorListener);
+
+        mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+                onIntent);
+        assertNotNull(mInjector.mSensorListener);
+
         mTracker.stop();
         assertNull(mInjector.mSensorListener);
         assertNull(mInjector.mSettingsObserver);
@@ -532,7 +549,6 @@
         mInjector.waitForHandler();
     }
 
-
     private static final class Idle implements MessageQueue.IdleHandler {
         private boolean mIdle;
 
@@ -565,6 +581,7 @@
         long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
         Handler mHandler;
         boolean mIdleScheduled;
+        boolean mInteractive = true;
 
         public TestInjector(Handler handler) {
             mHandler = handler;
@@ -577,7 +594,7 @@
 
         @Override
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener) {
+                SensorEventListener sensorListener, Handler handler) {
             mSensorListener = sensorListener;
         }
 
@@ -694,5 +711,9 @@
         public void cancelIdleJob(Context context) {
             mIdleScheduled = false;
         }
+
+        public boolean isInteractive(Context context) {
+            return mInteractive;
+        }
     }
 }
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java
index 7a2344317..90a373a 100644
--- a/tests/net/java/com/android/internal/util/RingBufferTest.java
+++ b/tests/net/java/com/android/internal/util/RingBufferTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
 import android.support.test.filters.SmallTest;
@@ -129,6 +130,55 @@
         assertArraysEqual(expected2, buffer.toArray());
     }
 
+    @Test
+    public void testGetNextSlot() {
+        int capacity = 100;
+        RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+        final DummyClass1[] actual = new DummyClass1[capacity];
+        final DummyClass1[] expected = new DummyClass1[capacity];
+        for (int i = 0; i < capacity; ++i) {
+            final DummyClass1 obj = buffer.getNextSlot();
+            obj.x = capacity * i;
+            actual[i] = obj;
+            expected[i] = new DummyClass1();
+            expected[i].x = capacity * i;
+        }
+        assertArraysEqual(expected, buffer.toArray());
+
+        for (int i = 0; i < capacity; ++i) {
+            if (actual[i] != buffer.getNextSlot()) {
+                fail("getNextSlot() should re-use objects if available");
+            }
+        }
+
+        RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(No nullary constructor)", buffer2.getNextSlot());
+
+        RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(Inaccessible class)", buffer3.getNextSlot());
+    }
+
+    public static final class DummyClass1 {
+        int x;
+
+        public boolean equals(Object o) {
+            if (o instanceof DummyClass1) {
+                final DummyClass1 other = (DummyClass1) o;
+                return other.x == this.x;
+            }
+            return false;
+        }
+    }
+
+    public static final class DummyClass2 {
+        public DummyClass2(int x) {}
+    }
+
+    private static final class DummyClass3 {}
+
     static <T> void assertArraysEqual(T[] expected, T[] got) {
         if (expected.length != got.length) {
             fail(Arrays.toString(expected) + " and " + Arrays.toString(got)