Merge "[AWARE] Metrics framework"
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 3e26e0f..9fb9c00 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -670,7 +670,7 @@
 
     int mNextFragmentIndex = 0;
     SparseArray<Fragment> mActive;
-    ArrayList<Fragment> mAdded;
+    final ArrayList<Fragment> mAdded = new ArrayList<>();
     ArrayList<BackStackRecord> mBackStack;
     ArrayList<Fragment> mCreatedMenus;
     
@@ -925,7 +925,7 @@
 
     @Override
     public List<Fragment> getFragments() {
-        if (mAdded == null) {
+        if (mAdded.isEmpty()) {
             return Collections.EMPTY_LIST;
         }
         synchronized (mAdded) {
@@ -988,15 +988,17 @@
             }
         }
 
-        if (mAdded != null) {
-            N = mAdded.size();
-            if (N > 0) {
-                writer.print(prefix); writer.println("Added Fragments:");
-                for (int i=0; i<N; i++) {
-                    Fragment f = mAdded.get(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(f.toString());
-                }
+        N = mAdded.size();
+        if (N > 0) {
+            writer.print(prefix);
+            writer.println("Added Fragments:");
+            for (int i = 0; i < N; i++) {
+                Fragment f = mAdded.get(i);
+                writer.print(prefix);
+                writer.print("  #");
+                writer.print(i);
+                writer.print(": ");
+                writer.println(f.toString());
             }
         }
 
@@ -1602,14 +1604,12 @@
             boolean loadersRunning = false;
 
             // Must add them in the proper order. mActive fragments may be out of order
-            if (mAdded != null) {
-                final int numAdded = mAdded.size();
-                for (int i = 0; i < numAdded; i++) {
-                    Fragment f = mAdded.get(i);
-                    moveFragmentToExpectedState(f);
-                    if (f.mLoaderManager != null) {
-                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
-                    }
+            final int numAdded = mAdded.size();
+            for (int i = 0; i < numAdded; i++) {
+                Fragment f = mAdded.get(i);
+                moveFragmentToExpectedState(f);
+                if (f.mLoaderManager != null) {
+                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                 }
             }
 
@@ -1675,9 +1675,6 @@
     }
     
     public void addFragment(Fragment fragment, boolean moveToStateNow) {
-        if (mAdded == null) {
-            mAdded = new ArrayList<Fragment>();
-        }
         if (DEBUG) Log.v(TAG, "add: " + fragment);
         makeActive(fragment);
         if (!fragment.mDetached) {
@@ -1713,10 +1710,8 @@
                     throw new IllegalStateException("Fragment not added: " + fragment);
                 }
             }
-            if (mAdded != null) {
-                synchronized (mAdded) {
-                    mAdded.remove(fragment);
-                }
+            synchronized (mAdded) {
+                mAdded.remove(fragment);
             }
             if (fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
@@ -1764,11 +1759,9 @@
             fragment.mDetached = true;
             if (fragment.mAdded) {
                 // We are not already in back stack, so need to remove the fragment.
-                if (mAdded != null) {
-                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
-                    synchronized (mAdded) {
-                        mAdded.remove(fragment);
-                    }
+                if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
+                synchronized (mAdded) {
+                    mAdded.remove(fragment);
                 }
                 if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
@@ -1783,9 +1776,6 @@
         if (fragment.mDetached) {
             fragment.mDetached = false;
             if (!fragment.mAdded) {
-                if (mAdded == null) {
-                    mAdded = new ArrayList<Fragment>();
-                }
                 if (mAdded.contains(fragment)) {
                     throw new IllegalStateException("Fragment already added: " + fragment);
                 }
@@ -1802,13 +1792,11 @@
     }
 
     public Fragment findFragmentById(int id) {
-        if (mAdded != null) {
-            // First look through added fragments.
-            for (int i=mAdded.size()-1; i>=0; i--) {
-                Fragment f = mAdded.get(i);
-                if (f != null && f.mFragmentId == id) {
-                    return f;
-                }
+        // First look through added fragments.
+        for (int i = mAdded.size() - 1; i >= 0; i--) {
+            Fragment f = mAdded.get(i);
+            if (f != null && f.mFragmentId == id) {
+                return f;
             }
         }
         if (mActive != null) {
@@ -1824,7 +1812,7 @@
     }
     
     public Fragment findFragmentByTag(String tag) {
-        if (mAdded != null && tag != null) {
+        if (tag != null) {
             // First look through added fragments.
             for (int i=mAdded.size()-1; i>=0; i--) {
                 Fragment f = mAdded.get(i);
@@ -1844,7 +1832,7 @@
         }
         return null;
     }
-    
+
     public Fragment findFragmentByWho(String who) {
         if (mActive != null && who != null) {
             for (int i=mActive.size()-1; i>=0; i--) {
@@ -2164,9 +2152,7 @@
         } else {
             mTmpAddedFragments.clear();
         }
-        if (mAdded != null) {
-            mTmpAddedFragments.addAll(mAdded);
-        }
+        mTmpAddedFragments.addAll(mAdded);
         Fragment oldPrimaryNav = getPrimaryNavigationFragment();
         for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
             final BackStackRecord record = records.get(recordNum);
@@ -2406,7 +2392,7 @@
         }
         // We want to leave the fragment in the started state
         final int state = Math.min(mCurState, Fragment.STARTED);
-        final int numAdded = mAdded == null ? 0 : mAdded.size();
+        final int numAdded = mAdded.size();
         for (int i = 0; i < numAdded; i++) {
             Fragment fragment = mAdded.get(i);
             if (fragment.mState < state) {
@@ -2754,23 +2740,21 @@
         BackStackState[] backStack = null;
         
         // Build list of currently added fragments.
-        if (mAdded != null) {
-            N = mAdded.size();
-            if (N > 0) {
-                added = new int[N];
-                for (int i=0; i<N; i++) {
-                    added[i] = mAdded.get(i).mIndex;
-                    if (added[i] < 0) {
-                        throwException(new IllegalStateException(
-                                "Failure saving state: active " + mAdded.get(i)
-                                + " has cleared index: " + added[i]));
-                    }
-                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
-                            + ": " + mAdded.get(i));
+        N = mAdded.size();
+        if (N > 0) {
+            added = new int[N];
+            for (int i=0; i<N; i++) {
+                added[i] = mAdded.get(i).mIndex;
+                if (added[i] < 0) {
+                    throwException(new IllegalStateException(
+                            "Failure saving state: active " + mAdded.get(i)
+                            + " has cleared index: " + added[i]));
                 }
+                if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+                        + ": " + mAdded.get(i));
             }
         }
-        
+
         // Now save back stack.
         if (mBackStack != null) {
             N = mBackStack.size();
@@ -2876,8 +2860,8 @@
         }
 
         // Build the list of currently added fragments.
+        mAdded.clear();
         if (fms.mAdded != null) {
-            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
             for (int i=0; i<fms.mAdded.length; i++) {
                 Fragment f = mActive.get(fms.mAdded[i]);
                 if (f == null) {
@@ -2893,8 +2877,6 @@
                     mAdded.add(f);
                 }
             }
-        } else {
-            mAdded = null;
         }
         
         // Build the back stack.
@@ -2970,7 +2952,7 @@
     public void noteStateNotSaved() {
         mSavedNonConfig = null;
         mStateSaved = false;
-        final int addedCount = mAdded == null ? 0 : mAdded.size();
+        final int addedCount = mAdded.size();
         for (int i = 0; i < addedCount; i++) {
             Fragment fragment = mAdded.get(i);
             if (fragment != null) {
@@ -3047,9 +3029,6 @@
      */
     @Deprecated
     public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        if (mAdded == null) {
-            return;
-        }
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3060,9 +3039,6 @@
 
     public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
             Configuration newConfig) {
-        if (mAdded == null) {
-            return;
-        }
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3076,9 +3052,6 @@
      */
     @Deprecated
     public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (mAdded == null) {
-            return;
-        }
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3089,9 +3062,6 @@
 
     public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode,
             Configuration newConfig) {
-        if (mAdded == null) {
-            return;
-        }
         for (int i = mAdded.size() - 1; i >= 0; --i) {
             final Fragment f = mAdded.get(i);
             if (f != null) {
@@ -3101,34 +3071,28 @@
     }
 
     public void dispatchConfigurationChanged(Configuration newConfig) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performConfigurationChanged(newConfig);
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performConfigurationChanged(newConfig);
             }
         }
     }
 
     public void dispatchLowMemory() {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performLowMemory();
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performLowMemory();
             }
         }
     }
 
     public void dispatchTrimMemory(int level) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performTrimMemory(level);
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performTrimMemory(level);
             }
         }
     }
@@ -3136,21 +3100,19 @@
     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         boolean show = false;
         ArrayList<Fragment> newMenus = null;
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performCreateOptionsMenu(menu, inflater)) {
-                        show = true;
-                        if (newMenus == null) {
-                            newMenus = new ArrayList<Fragment>();
-                        }
-                        newMenus.add(f);
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performCreateOptionsMenu(menu, inflater)) {
+                    show = true;
+                    if (newMenus == null) {
+                        newMenus = new ArrayList<Fragment>();
                     }
+                    newMenus.add(f);
                 }
             }
         }
-        
+
         if (mCreatedMenus != null) {
             for (int i=0; i<mCreatedMenus.size(); i++) {
                 Fragment f = mCreatedMenus.get(i);
@@ -3167,13 +3129,11 @@
     
     public boolean dispatchPrepareOptionsMenu(Menu menu) {
         boolean show = false;
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performPrepareOptionsMenu(menu)) {
-                        show = true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performPrepareOptionsMenu(menu)) {
+                    show = true;
                 }
             }
         }
@@ -3181,13 +3141,11 @@
     }
     
     public boolean dispatchOptionsItemSelected(MenuItem item) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performOptionsItemSelected(item)) {
-                        return true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performOptionsItemSelected(item)) {
+                    return true;
                 }
             }
         }
@@ -3195,13 +3153,11 @@
     }
     
     public boolean dispatchContextItemSelected(MenuItem item) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    if (f.performContextItemSelected(item)) {
-                        return true;
-                    }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                if (f.performContextItemSelected(item)) {
+                    return true;
                 }
             }
         }
@@ -3209,12 +3165,10 @@
     }
     
     public void dispatchOptionsMenuClosed(Menu menu) {
-        if (mAdded != null) {
-            for (int i=0; i<mAdded.size(); i++) {
-                Fragment f = mAdded.get(i);
-                if (f != null) {
-                    f.performOptionsMenuClosed(menu);
-                }
+        for (int i = 0; i < mAdded.size(); i++) {
+            Fragment f = mAdded.get(i);
+            if (f != null) {
+                f.performOptionsMenuClosed(menu);
             }
         }
     }
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index e6e448e..c9eecf1 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -88,7 +88,7 @@
     }
 
     @SuppressWarnings("deprecation")
-    public static class DeviceCaptureCallbackProxy extends CameraDeviceImpl.CaptureCallback {
+    public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
         private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
 
         public DeviceCaptureCallbackProxy(
@@ -138,6 +138,13 @@
                 int sequenceId) {
             mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
         }
+
+        @Override
+        public void onCaptureBufferLost(CameraDevice camera,
+                CaptureRequest request, Surface target, long frameNumber) {
+            mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
+        }
+
     }
 
     public static class SessionStateCallbackProxy
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 16ffee0..b3938cb 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -452,6 +452,37 @@
     private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
             Handler handler, CaptureCallback callback) {
         CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+
+            @Override
+            public void onCaptureStarted(CameraDevice camera,
+                    CaptureRequest request, long timestamp, long frameNumber) {
+                // Do nothing
+            }
+
+            @Override
+            public void onCapturePartial(CameraDevice camera,
+                    CaptureRequest request, android.hardware.camera2.CaptureResult result) {
+                // Do nothing
+            }
+
+            @Override
+            public void onCaptureProgressed(CameraDevice camera,
+                    CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
+                // Do nothing
+            }
+
+            @Override
+            public void onCaptureCompleted(CameraDevice camera,
+                    CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
+                // Do nothing
+            }
+
+            @Override
+            public void onCaptureFailed(CameraDevice camera,
+                    CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
+                // Do nothing
+            }
+
             @Override
             public void onCaptureSequenceCompleted(CameraDevice camera,
                     int sequenceId, long frameNumber) {
@@ -463,6 +494,13 @@
                     int sequenceId) {
                 finishPendingSequence(sequenceId);
             }
+
+            @Override
+            public void onCaptureBufferLost(CameraDevice camera,
+                    CaptureRequest request, Surface target, long frameNumber) {
+                // Do nothing
+            }
+
         };
 
         /*
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ab87f15..0d5c5e3 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1112,8 +1112,11 @@
      * <p>A callback for tracking the progress of a {@link CaptureRequest}
      * submitted to the camera device.</p>
      *
+     * An interface instead of an abstract class because this is internal and
+     * we want to make sure we always implement all its callbacks until we reach
+     * the public layer.
      */
-    public static abstract class CaptureCallback {
+    public interface CaptureCallback {
 
         /**
          * This constant is used to indicate that no images were captured for
@@ -1130,9 +1133,7 @@
          * @see android.media.MediaActionSound
          */
         public void onCaptureStarted(CameraDevice camera,
-                CaptureRequest request, long timestamp, long frameNumber) {
-            // default empty implementation
-        }
+                CaptureRequest request, long timestamp, long frameNumber);
 
         /**
          * This method is called when some results from an image capture are
@@ -1141,9 +1142,7 @@
          * @hide
          */
         public void onCapturePartial(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
-            // default empty implementation
-        }
+                CaptureRequest request, CaptureResult result);
 
         /**
          * This method is called when an image capture makes partial forward progress; some
@@ -1151,18 +1150,14 @@
          *
          */
         public void onCaptureProgressed(CameraDevice camera,
-                CaptureRequest request, CaptureResult partialResult) {
-            // default empty implementation
-        }
+                CaptureRequest request, CaptureResult partialResult);
 
         /**
          * This method is called when an image capture has fully completed and all the
          * result metadata is available.
          */
         public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, TotalCaptureResult result) {
-            // default empty implementation
-        }
+                CaptureRequest request, TotalCaptureResult result);
 
         /**
          * This method is called instead of {@link #onCaptureCompleted} when the
@@ -1170,9 +1165,7 @@
          * request.
          */
         public void onCaptureFailed(CameraDevice camera,
-                CaptureRequest request, CaptureFailure failure) {
-            // default empty implementation
-        }
+                CaptureRequest request, CaptureFailure failure);
 
         /**
          * This method is called independently of the others in CaptureCallback,
@@ -1180,9 +1173,7 @@
          * or {@link CaptureFailure} for it have been returned via this callback.
          */
         public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, long frameNumber) {
-            // default empty implementation
-        }
+                int sequenceId, long frameNumber);
 
         /**
          * This method is called independently of the others in CaptureCallback,
@@ -1190,14 +1181,16 @@
          * or {@link CaptureFailure} for it have been returned via this callback.
          */
         public void onCaptureSequenceAborted(CameraDevice camera,
-                int sequenceId) {
-            // default empty implementation
-        }
+                int sequenceId);
 
+        /**
+         * This method is called independently of the others in CaptureCallback, if an output buffer
+         * is dropped for a particular capture request.
+         *
+         * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss.
+         */
         public void onCaptureBufferLost(CameraDevice camera,
-                CaptureRequest request, Surface target, long frameNumber) {
-            // default empty implementation
-        }
+                CaptureRequest request, Surface target, long frameNumber);
     }
 
     /**
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b00e65a..324a08c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -932,7 +932,6 @@
             } else if (mAuthenticationCallback != null) {
                 mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
                         getErrorString(errMsgId, vendorCode));
-                mAuthenticationCallback = null;
             } else if (mRemovalCallback != null) {
                 mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
                         getErrorString(errMsgId, vendorCode));
@@ -953,7 +952,6 @@
                 final AuthenticationResult result =
                         new AuthenticationResult(mCryptoObject, fp, userId);
                 mAuthenticationCallback.onAuthenticationSucceeded(result);
-                mAuthenticationCallback = null;
             }
         }
 
diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java
index 7b845be..bccaf60 100644
--- a/core/java/android/net/nsd/NsdServiceInfo.java
+++ b/core/java/android/net/nsd/NsdServiceInfo.java
@@ -30,7 +30,6 @@
 import java.util.Collections;
 import java.util.Map;
 
-
 /**
  * A class representing service information for network service discovery
  * {@see NsdManager}
@@ -43,7 +42,7 @@
 
     private String mServiceType;
 
-    private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<String, byte[]>();
+    private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
 
     private InetAddress mHost;
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 4bad7ab..a2fb9db 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -807,8 +807,20 @@
     }
 
     /**
-     * Verifies the the current flash of the device is consistent with what
+     * True if Treble is enabled and required for this device.
+     *
+     * @hide
+     */
+    public static final boolean IS_TREBLE_ENABLED =
+        SystemProperties.getBoolean("ro.treble.enabled", false);
+
+    /**
+     * Verifies the current flash of the device is consistent with what
      * was expected at build time.
+     *
+     * Treble devices will verify the Vendor Interface (VINTF). A device
+     * launched without Treble:
+     *
      * 1) Checks that device fingerprint is defined and that it matches across
      *    various partitions.
      * 2) Verifies radio and bootloader partitions are those expected in the build.
@@ -819,6 +831,17 @@
         // Don't care on eng builds.  Incremental build may trigger false negative.
         if (IS_ENG) return true;
 
+        if (IS_TREBLE_ENABLED) {
+            int result = VintfObject.verify(new String[0]);
+
+            if (result != 0) {
+                Slog.e(TAG, "Vendor interface is incompatible, error="
+                        + String.valueOf(result));
+            }
+
+            return result == 0;
+        }
+
         final String system = SystemProperties.get("ro.build.fingerprint");
         final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
         final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint");
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index e063855..cf8f3eb 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -22,9 +22,12 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
 
 import libcore.io.Streams;
 
@@ -570,7 +573,16 @@
 
             // Having set up the BCB (bootloader control block), go ahead and reboot
             PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-            pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
+            String reason = PowerManager.REBOOT_RECOVERY_UPDATE;
+
+            // On TV, reboot quiescently if the screen is off
+            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+                WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+                if (wm.getDefaultDisplay().getState() != Display.STATE_ON) {
+                    reason += ",quiescent";
+                }
+            }
+            pm.reboot(reason);
 
             throw new IOException("Reboot failed (no permissions?)");
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7bd3bf6..7e03e42 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -899,11 +899,8 @@
      * @return the user name
      */
     public String getUserName() {
-        try {
-            return mService.getUserInfo(getUserHandle()).name;
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        UserInfo user = getUserInfo(getUserHandle());
+        return user == null ? "" : user.name;
     }
 
     /**
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 0a294ab..2b03ed6 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -16,24 +16,17 @@
 
 package android.util;
 
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.os.SystemClock;
 
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
-import java.util.TimeZone;
-
+import java.util.List;
+import libcore.util.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 /**
@@ -44,14 +37,9 @@
     private static final boolean DBG = false;
     private static final String TAG = "TimeUtils";
 
-    /** Cached results of getTineZones */
-    private static final Object sLastLockObj = new Object();
-    private static ArrayList<TimeZone> sLastZones = null;
-    private static String sLastCountry = null;
-
     /** Cached results of getTimeZonesWithUniqueOffsets */
     private static final Object sLastUniqueLockObj = new Object();
-    private static ArrayList<TimeZone> sLastUniqueZoneOffsets = null;
+    private static List<String> sLastUniqueZoneOffsets = null;
     private static String sLastUniqueCountry = null;
 
     /** {@hide} */
@@ -62,50 +50,39 @@
      * and DST value at the specified moment in the specified country.
      * Returns null if no suitable zone could be found.
      */
-    public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
-        TimeZone best = null;
-        final Date d = new Date(when);
+    public static java.util.TimeZone getTimeZone(
+            int offset, boolean dst, long when, String country) {
 
-        TimeZone current = TimeZone.getDefault();
-        String currentName = current.getID();
-        int currentOffset = current.getOffset(when);
-        boolean currentDst = current.inDaylightTime(d);
-
-        for (TimeZone tz : getTimeZones(country)) {
-            // If the current time zone is from the right country
-            // and meets the other known properties, keep it
-            // instead of changing to another one.
-
-            if (tz.getID().equals(currentName)) {
-                if (currentOffset == offset && currentDst == dst) {
-                    return current;
-                }
-            }
-
-            // Otherwise, take the first zone from the right
-            // country that has the correct current offset and DST.
-            // (Keep iterating instead of returning in case we
-            // haven't encountered the current time zone yet.)
-
-            if (best == null) {
-                if (tz.getOffset(when) == offset &&
-                    tz.inDaylightTime(d) == dst) {
-                    best = tz;
-                }
-            }
-        }
-
-        return best;
+        android.icu.util.TimeZone icuTimeZone = getIcuTimeZone(offset, dst, when, country);
+        // We must expose a java.util.TimeZone here for API compatibility because this is a public
+        // API method.
+        return icuTimeZone != null ? java.util.TimeZone.getTimeZone(icuTimeZone.getID()) : null;
     }
 
     /**
-     * Return list of unique time zones for the country. Do not modify
+     * Tries to return a frozen ICU time zone that would have had the specified offset
+     * and DST value at the specified moment in the specified country.
+     * Returns null if no suitable zone could be found.
+     */
+    private static android.icu.util.TimeZone getIcuTimeZone(
+            int offset, boolean dst, long when, String country) {
+        if (country == null) {
+            return null;
+        }
+
+        android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
+        return TimeZoneFinder.getInstance()
+                .lookupTimeZoneByCountryAndOffset(country, offset, dst, when, bias);
+    }
+
+    /**
+     * Returns an immutable list of unique time zone IDs for the country.
      *
      * @param country to find
-     * @return list of unique time zones, maybe empty but never null. Do not modify.
+     * @return unmodifiable list of unique time zones, maybe empty but never null.
      * @hide
      */
-    public static ArrayList<TimeZone> getTimeZonesWithUniqueOffsets(String country) {
+    public static List<String> getTimeZoneIdsWithUniqueOffsets(String country) {
         synchronized(sLastUniqueLockObj) {
             if ((country != null) && country.equals(sLastUniqueCountry)) {
                 if (DBG) {
@@ -116,9 +93,9 @@
             }
         }
 
-        Collection<TimeZone> zones = getTimeZones(country);
-        ArrayList<TimeZone> uniqueTimeZones = new ArrayList<TimeZone>();
-        for (TimeZone zone : zones) {
+        Collection<android.icu.util.TimeZone> zones = getIcuTimeZones(country);
+        ArrayList<android.icu.util.TimeZone> uniqueTimeZones = new ArrayList<>();
+        for (android.icu.util.TimeZone zone : zones) {
             // See if we already have this offset,
             // Using slow but space efficient and these are small.
             boolean found = false;
@@ -128,7 +105,7 @@
                     break;
                 }
             }
-            if (found == false) {
+            if (!found) {
                 if (DBG) {
                     Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
                             zone.getRawOffset() + " zone.getID=" + zone.getID());
@@ -139,81 +116,43 @@
 
         synchronized(sLastUniqueLockObj) {
             // Cache the last result
-            sLastUniqueZoneOffsets = uniqueTimeZones;
+            sLastUniqueZoneOffsets = extractZoneIds(uniqueTimeZones);
             sLastUniqueCountry = country;
 
             return sLastUniqueZoneOffsets;
         }
     }
 
+    private static List<String> extractZoneIds(List<android.icu.util.TimeZone> timeZones) {
+        List<String> ids = new ArrayList<>(timeZones.size());
+        for (android.icu.util.TimeZone timeZone : timeZones) {
+            ids.add(timeZone.getID());
+        }
+        return Collections.unmodifiableList(ids);
+    }
+
     /**
-     * Returns the time zones for the country, which is the code
-     * attribute of the timezone element in time_zones_by_country.xml. Do not modify.
+     * Returns an immutable list of frozen ICU time zones for the country.
      *
-     * @param country is a two character country code.
-     * @return TimeZone list, maybe empty but never null. Do not modify.
+     * @param countryIso is a two character country code.
+     * @return TimeZone list, maybe empty but never null.
      * @hide
      */
-    public static ArrayList<TimeZone> getTimeZones(String country) {
-        synchronized (sLastLockObj) {
-            if ((country != null) && country.equals(sLastCountry)) {
-                if (DBG) Log.d(TAG, "getTimeZones(" + country + "): return cached version");
-                return sLastZones;
+    private static List<android.icu.util.TimeZone> getIcuTimeZones(String countryIso) {
+        if (countryIso == null) {
+            if (DBG) Log.d(TAG, "getIcuTimeZones(null): return empty list");
+            return Collections.emptyList();
+        }
+        List<android.icu.util.TimeZone> timeZones =
+                TimeZoneFinder.getInstance().lookupTimeZonesByCountry(countryIso);
+        if (timeZones == null) {
+            if (DBG) {
+                Log.d(TAG, "getIcuTimeZones(" + countryIso
+                        + "): returned null, converting to empty list");
             }
+            return Collections.emptyList();
         }
-
-        ArrayList<TimeZone> tzs = new ArrayList<TimeZone>();
-
-        if (country == null) {
-            if (DBG) Log.d(TAG, "getTimeZones(null): return empty list");
-            return tzs;
-        }
-
-        Resources r = Resources.getSystem();
-        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
-
-        try {
-            XmlUtils.beginDocument(parser, "timezones");
-
-            while (true) {
-                XmlUtils.nextElement(parser);
-
-                String element = parser.getName();
-                if (element == null || !(element.equals("timezone"))) {
-                    break;
-                }
-
-                String code = parser.getAttributeValue(null, "code");
-
-                if (country.equals(code)) {
-                    if (parser.next() == XmlPullParser.TEXT) {
-                        String zoneIdString = parser.getText();
-                        TimeZone tz = TimeZone.getTimeZone(zoneIdString);
-                        if (tz.getID().startsWith("GMT") == false) {
-                            // tz.getID doesn't start not "GMT" so its valid
-                            tzs.add(tz);
-                            if (DBG) {
-                                Log.d(TAG, "getTimeZone('" + country + "'): found tz.getID=="
-                                    + ((tz != null) ? tz.getID() : "<no tz>"));
-                            }
-                        }
-                    }
-                }
-            }
-        } catch (XmlPullParserException e) {
-            Log.e(TAG, "Got xml parser exception getTimeZone('" + country + "'): e=", e);
-        } catch (IOException e) {
-            Log.e(TAG, "Got IO exception getTimeZone('" + country + "'): e=", e);
-        } finally {
-            parser.close();
-        }
-
-        synchronized(sLastLockObj) {
-            // Cache the last result;
-            sLastZones = tzs;
-            sLastCountry = country;
-            return sLastZones;
-        }
+        return timeZones;
     }
 
     /**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 4402fb1..da81efc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -894,6 +894,15 @@
         }
     }
 
+    /**
+     * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given
+     * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and
+     * not the RenderNode from a View.
+     **/
+    public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
+        return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1037,4 +1046,6 @@
 
     private static native int nCopySurfaceInto(Surface surface,
             int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap);
+
+    private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 54ec898..912a16d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -593,12 +593,12 @@
  * a single tag using the {@link android.R.styleable#View_tag android:tag}
  * attribute or multiple tags using the {@code <tag>} child element:
  * <pre>
- *     &ltView ...
+ *     &lt;View ...
  *           android:tag="@string/mytag_value" /&gt;
- *     &ltView ...&gt;
- *         &lttag android:id="@+id/mytag"
+ *     &lt;View ...&gt;
+ *         &lt;tag android:id="@+id/mytag"
  *              android:value="@string/mytag_value" /&gt;
- *     &lt/View>
+ *     &lt;/View>
  * </pre>
  * </p>
  * <p>
@@ -628,11 +628,11 @@
  * {@link android.R.styleable#Theme_colorAccent android:colorAccent} defined on
  * the inflation context's theme (e.g. the Activity theme) will be preserved.
  * <pre>
- *     &ltLinearLayout
+ *     &lt;LinearLayout
  *             ...
  *             android:theme="@android:theme/ThemeOverlay.Material.Dark"&gt;
- *         &ltView ...&gt;
- *     &lt/LinearLayout&gt;
+ *         &lt;View ...&gt;
+ *     &lt;/LinearLayout&gt;
  * </pre>
  * </p>
  *
@@ -7514,15 +7514,22 @@
      * <li>Passing the actual value to the equivalent setter in the view.
      * </ol>
      *
-     * <p>For example, a text-field view would call:
+     * <p>For example, a text-field view could implement the method this way:
+     *
      * <pre class="prettyprint">
-     * CharSequence text = value.getTextValue();
-     * if (text != null) {
-     *     setText(text);
+     * &#64;Override
+     * public void autofill(AutofillValue value) {
+     *   if (!value.isText() || !this.isEditable()) {
+     *      return;
+     *   }
+     *   CharSequence text = value.getTextValue();
+     *   if (text != null) {
+     *     this.setText(text);
+     *   }
      * }
      * </pre>
      *
-     * <p>If the value is updated asyncronously the next call to
+     * <p>If the value is updated asynchronously the next call to
      * {@link AutofillManager#notifyValueChanged(View)} must happen <u>after</u> the value was
      * changed to the autofilled value. If not, the view will not be considered autofilled.
      *
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b0a2e11..7e71ce9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -291,4 +291,13 @@
         // GraphicsJNI.h includes hwui headers
         "libhwui",
     ],
+
+    product_variables: {
+        debuggable: {
+            cflags: ["-D__ANDROID_DEBUGGABLE__"]
+        },
+        treble: {
+            cflags: ["-D__ANDROID_TREBLE__"]
+        },
+    },
 }
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index dcb2300..19f779f 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -23,6 +23,8 @@
 #include "android_os_HwParcel.h"
 #include "android_os_HwRemoteBinder.h"
 
+#include <cstring>
+
 #include <JNIHelp.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android/hidl/base/1.0/IBase.h>
@@ -331,8 +333,19 @@
 
     IServiceManager::Transport transport = transportRet;
 
-    if (   transport != IServiceManager::Transport::EMPTY
-        && transport != IServiceManager::Transport::HWBINDER) {
+#ifdef __ANDROID_TREBLE__
+#ifdef __ANDROID_DEBUGGABLE__
+    const char* testingOverride = std::getenv("TREBLE_TESTING_OVERRIDE");
+    const bool vintfLegacy = (transport == IServiceManager::Transport::EMPTY)
+            && testingOverride && !strcmp(testingOverride, "true");
+#else // __ANDROID_TREBLE__ but not __ANDROID_DEBUGGABLE__
+    const bool vintfLegacy = false;
+#endif // __ANDROID_DEBUGGABLE__
+#else // not __ANDROID_TREBLE__
+    const bool vintfLegacy = (transport == IServiceManager::Transport::EMPTY);
+#endif // __ANDROID_TREBLE__";
+
+    if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) {
         LOG(ERROR) << "service " << ifaceName << " declares transport method "
                    << toString(transport) << " but framework expects hwbinder.";
         signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index aa1893c..4c530d7 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -25,6 +25,10 @@
 #include <GraphicsJNI.h>
 #include <ScopedPrimitiveArray.h>
 
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/Surface.h>
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <private/EGL/cache.h>
@@ -839,6 +843,75 @@
     return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap);
 }
 
+class ContextFactory : public IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+        return new AnimationContext(clock);
+    }
+};
+
+static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    if (jwidth <= 0 || jheight <= 0) {
+        ALOGW("Invalid width %d or height %d", jwidth, jheight);
+        return nullptr;
+    }
+
+    uint32_t width = jwidth;
+    uint32_t height = jheight;
+
+    // Create a Surface wired up to a BufferItemConsumer
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> rawConsumer;
+    BufferQueue::createBufferQueue(&producer, &rawConsumer);
+    rawConsumer->setMaxBufferCount(1);
+    sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer,
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER);
+    consumer->setDefaultBufferSize(width, height);
+    sp<Surface> surface = new Surface(producer);
+
+    // Render into the surface
+    {
+        ContextFactory factory;
+        RenderProxy proxy{false, renderNode, &factory};
+        proxy.loadSystemProperties();
+        proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
+        proxy.initialize(surface);
+        // Shadows can't be used via this interface, so just set the light source
+        // to all 0s.
+        proxy.setup(0, 0, 0);
+        proxy.setLightCenter((Vector3){0, 0, 0});
+        nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+        UiFrameInfoBuilder(proxy.frameInfo())
+                .setVsync(vsync, vsync)
+                .addFlag(FrameInfoFlags::SurfaceCanvas);
+        proxy.syncAndDrawFrame();
+    }
+
+    // Yank out the GraphicBuffer
+    BufferItem bufferItem;
+    status_t err;
+    if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) {
+        ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err));
+        return nullptr;
+    }
+    sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer;
+    // We don't really care if this fails or not since we're just going to destroy this anyway
+    consumer->releaseBuffer(bufferItem);
+    if (!buffer.get()) {
+        ALOGW("GraphicBuffer is null?");
+        return nullptr;
+    }
+    if (buffer->getWidth() != width || buffer->getHeight() != height) {
+        ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d",
+                buffer->getWidth(), buffer->getHeight(), width, height);
+        // Continue I guess?
+    }
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+    return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable);
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -934,6 +1007,8 @@
             (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
     { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I",
                 (void*)android_view_ThreadedRenderer_copySurfaceInto },
+    { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
+            (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index aa586b7..1cdaed2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1479,7 +1479,6 @@
   <java-symbol type="xml" name="password_kbd_symbols" />
   <java-symbol type="xml" name="password_kbd_symbols_shift" />
   <java-symbol type="xml" name="power_profile" />
-  <java-symbol type="xml" name="time_zones_by_country" />
   <java-symbol type="xml" name="sms_short_codes" />
   <java-symbol type="xml" name="audio_assets" />
   <java-symbol type="xml" name="global_keys" />
diff --git a/core/res/res/xml/time_zones_by_country.xml b/core/res/res/xml/time_zones_by_country.xml
deleted file mode 100644
index 22bfea1..0000000
--- a/core/res/res/xml/time_zones_by_country.xml
+++ /dev/null
@@ -1,1372 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2006, 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.
-*/
--->
-<timezones>
-    <!-- ANDORRA, 1:00 -->
-
-    <timezone code="ad">Europe/Andorra</timezone>
-
-    <!-- UNITED ARAB EMIRATES, 4:00 -->
-
-    <timezone code="ae">Asia/Dubai</timezone>
-
-    <!-- AFGHANISTAN, 4:30 -->
-
-    <timezone code="af">Asia/Kabul</timezone>
-
-    <!-- ANTIGUA AND BARBUDA, -4:00 -->
-
-    <timezone code="ag">America/Antigua</timezone>
-
-    <!-- ANGUILLA, -4:00 -->
-
-    <timezone code="ai">America/Anguilla</timezone>
-
-    <!-- ALBANIA, 1:00 -->
-
-    <timezone code="al">Europe/Tirane</timezone>
-
-    <!-- ARMENIA, 4:00 -->
-
-    <timezone code="am">Asia/Yerevan</timezone>
-
-    <!-- ANGOLA, 1:00 -->
-
-    <timezone code="ao">Africa/Luanda</timezone>
-
-    <!-- ANTARCTICA, 12:00 -->
-
-    <timezone code="aq">Antarctica/McMurdo</timezone>
-
-    <!-- ANTARCTICA, 10:00 -->
-
-    <timezone code="aq">Antarctica/DumontDUrville</timezone>
-
-    <!-- ANTARCTICA, 8:00 -->
-
-    <timezone code="aq">Antarctica/Casey</timezone>
-
-    <!-- ANTARCTICA, 7:00 -->
-
-    <timezone code="aq">Antarctica/Davis</timezone>
-
-    <!-- ANTARCTICA, 5:00 -->
-
-    <timezone code="aq">Antarctica/Mawson</timezone>
-
-    <!-- ANTARCTICA, 6:00 -->
-
-    <timezone code="aq">Antarctica/Vostok</timezone>
-
-    <!-- ANTARCTICA, 3:00 -->
-
-    <timezone code="aq">Antarctica/Syowa</timezone>
-
-    <!-- ANTARCTICA, 0:00 -->
-
-    <timezone code="aq">Antarctica/Troll</timezone>
-
-    <!-- ANTARCTICA, -3:00 -->
-
-    <timezone code="aq">Antarctica/Rothera</timezone>
-
-    <!-- ANTARCTICA, -4:00 -->
-
-    <timezone code="aq">Antarctica/Palmer</timezone>
-
-    <!-- ARGENTINA, -3:00 -->
-
-    <timezone code="ar">America/Argentina/Buenos_Aires</timezone>
-    <timezone code="ar">America/Argentina/Cordoba</timezone>
-    <timezone code="ar">America/Argentina/Salta</timezone>
-    <timezone code="ar">America/Argentina/Jujuy</timezone>
-    <timezone code="ar">America/Argentina/Tucuman</timezone>
-    <timezone code="ar">America/Argentina/Catamarca</timezone>
-    <timezone code="ar">America/Argentina/La_Rioja</timezone>
-    <timezone code="ar">America/Argentina/San_Juan</timezone>
-    <timezone code="ar">America/Argentina/Mendoza</timezone>
-    <timezone code="ar">America/Argentina/San_Luis</timezone>
-    <timezone code="ar">America/Argentina/Rio_Gallegos</timezone>
-    <timezone code="ar">America/Argentina/Ushuaia</timezone>
-
-    <!-- AMERICAN SAMOA, -11:00 -->
-
-    <timezone code="as">Pacific/Pago_Pago</timezone>
-
-    <!-- AUSTRIA, 1:00 -->
-
-    <timezone code="at">Europe/Vienna</timezone>
-
-    <!-- AUSTRALIA, 10:00 -->
-
-    <timezone code="au">Australia/Sydney</timezone>
-    <timezone code="au">Australia/Melbourne</timezone>
-    <timezone code="au">Australia/Brisbane</timezone>
-    <timezone code="au">Australia/Hobart</timezone>
-    <timezone code="au">Australia/Currie</timezone>
-    <timezone code="au">Australia/Lindeman</timezone>
-
-    <!-- AUSTRALIA, 11:00 -->
-    <timezone code="au">Antarctica/Macquarie</timezone>
-
-    <!-- AUSTRALIA, 10:30 -->
-
-    <timezone code="au">Australia/Lord_Howe</timezone>
-
-    <!-- AUSTRALIA, 9:30 -->
-
-    <timezone code="au">Australia/Adelaide</timezone>
-    <timezone code="au">Australia/Broken_Hill</timezone>
-    <timezone code="au">Australia/Darwin</timezone>
-
-    <!-- AUSTRALIA, 8:00 -->
-
-    <timezone code="au">Australia/Perth</timezone>
-
-    <!-- AUSTRALIA, 8:45 -->
-
-    <timezone code="au">Australia/Eucla</timezone>
-
-    <!-- ARUBA, -4:00 -->
-
-    <timezone code="aw">America/Aruba</timezone>
-
-    <!-- ALAND ISLANDS, 2:00 -->
-
-    <timezone code="ax">Europe/Mariehamn</timezone>
-
-    <!-- AZERBAIJAN, 4:00 -->
-
-    <timezone code="az">Asia/Baku</timezone>
-
-    <!-- BOSNIA AND HERZEGOVINA, 1:00 -->
-
-    <timezone code="ba">Europe/Sarajevo</timezone>
-
-    <!-- BARBADOS, -4:00 -->
-
-    <timezone code="bb">America/Barbados</timezone>
-
-    <!-- BANGLADESH, 6:00 -->
-
-    <timezone code="bd">Asia/Dhaka</timezone>
-
-    <!-- BELGIUM, 1:00 -->
-
-    <timezone code="be">Europe/Brussels</timezone>
-
-    <!-- BURKINA FASO, 0:00 -->
-
-    <timezone code="bf">Africa/Ouagadougou</timezone>
-
-    <!-- BULGARIA, 2:00 -->
-
-    <timezone code="bg">Europe/Sofia</timezone>
-
-    <!-- BAHRAIN, 3:00 -->
-
-    <timezone code="bh">Asia/Bahrain</timezone>
-
-    <!-- BURUNDI, 2:00 -->
-
-    <timezone code="bi">Africa/Bujumbura</timezone>
-
-    <!-- BENIN, 1:00 -->
-
-    <timezone code="bj">Africa/Porto-Novo</timezone>
-
-    <!-- Saint Barthélemy, -4:00 -->
-
-    <timezone code="bl">America/St_Barthelemy</timezone>
-
-    <!-- BERMUDA, -4:00 -->
-
-    <timezone code="bm">Atlantic/Bermuda</timezone>
-
-    <!-- BRUNEI DARUSSALAM, 8:00 -->
-
-    <timezone code="bn">Asia/Brunei</timezone>
-
-    <!-- BOLIVIA, -4:00 -->
-
-    <timezone code="bo">America/La_Paz</timezone>
-
-    <!-- Caribbean Netherlands, -4:00 -->
-
-    <timezone code="bq">America/Kralendijk</timezone>
-
-    <!-- BRAZIL, -2:00 -->
-
-    <timezone code="br">America/Noronha</timezone>
-
-    <!-- BRAZIL, -3:00 -->
-
-    <timezone code="br">America/Sao_Paulo</timezone>
-    <timezone code="br">America/Belem</timezone>
-    <timezone code="br">America/Fortaleza</timezone>
-    <timezone code="br">America/Recife</timezone>
-    <timezone code="br">America/Araguaina</timezone>
-    <timezone code="br">America/Maceio</timezone>
-    <timezone code="br">America/Bahia</timezone>
-    <timezone code="br">America/Santarem</timezone>
-
-    <!-- BRAZIL, -4:00 -->
-
-    <timezone code="br">America/Manaus</timezone>
-    <timezone code="br">America/Campo_Grande</timezone>
-    <timezone code="br">America/Cuiaba</timezone>
-    <timezone code="br">America/Porto_Velho</timezone>
-    <timezone code="br">America/Boa_Vista</timezone>
-
-    <!-- BRAZIL, -5:00 -->
-
-    <timezone code="br">America/Eirunepe</timezone>
-    <timezone code="br">America/Rio_Branco</timezone>
-
-    <!-- BAHAMAS, -5:00 -->
-
-    <timezone code="bs">America/Nassau</timezone>
-
-    <!-- BHUTAN, 6:00 -->
-
-    <timezone code="bt">Asia/Thimphu</timezone>
-
-    <!-- BOTSWANA, 2:00 -->
-
-    <timezone code="bw">Africa/Gaborone</timezone>
-
-    <!-- BELARUS, 3:00 -->
-
-    <timezone code="by">Europe/Minsk</timezone>
-
-    <!-- BELIZE, -6:00 -->
-
-    <timezone code="bz">America/Belize</timezone>
-
-    <!-- CANADA, -3:30 -->
-
-    <timezone code="ca">America/St_Johns</timezone>
-
-    <!-- CANADA, -4:00 -->
-
-    <timezone code="ca">America/Halifax</timezone>
-    <timezone code="ca">America/Glace_Bay</timezone>
-    <timezone code="ca">America/Moncton</timezone>
-    <timezone code="ca">America/Goose_Bay</timezone>
-    <timezone code="ca">America/Blanc-Sablon</timezone>
-
-    <!-- CANADA, -5:00 -->
-
-    <timezone code="ca">America/Toronto</timezone>
-    <timezone code="ca">America/Nipigon</timezone>
-    <timezone code="ca">America/Thunder_Bay</timezone>
-    <timezone code="ca">America/Iqaluit</timezone>
-    <timezone code="ca">America/Pangnirtung</timezone>
-    <timezone code="ca">America/Atikokan</timezone>
-
-    <!-- CANADA, -6:00 -->
-
-    <timezone code="ca">America/Winnipeg</timezone>
-    <timezone code="ca">America/Regina</timezone>
-    <timezone code="ca">America/Rankin_Inlet</timezone>
-    <timezone code="ca">America/Rainy_River</timezone>
-    <timezone code="ca">America/Swift_Current</timezone>
-    <timezone code="ca">America/Resolute</timezone>
-
-    <!-- CANADA, -7:00 -->
-
-    <timezone code="ca">America/Edmonton</timezone>
-    <timezone code="ca">America/Cambridge_Bay</timezone>
-    <timezone code="ca">America/Yellowknife</timezone>
-    <timezone code="ca">America/Inuvik</timezone>
-    <timezone code="ca">America/Dawson_Creek</timezone>
-    <timezone code="ca">America/Creston</timezone>
-    <timezone code="ca">America/Fort_Nelson</timezone>
-
-    <!-- CANADA, -8:00 -->
-
-    <timezone code="ca">America/Vancouver</timezone>
-    <timezone code="ca">America/Whitehorse</timezone>
-    <timezone code="ca">America/Dawson</timezone>
-
-    <!-- COCOS (KEELING) ISLANDS, 6:30 -->
-
-    <timezone code="cc">Indian/Cocos</timezone>
-
-    <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 2:00 -->
-
-    <timezone code="cd">Africa/Lubumbashi</timezone>
-
-    <!-- CONGO, THE DEMOCRATIC REPUBLIC OF THE, 1:00 -->
-
-    <timezone code="cd">Africa/Kinshasa</timezone>
-
-    <!-- CENTRAL AFRICAN REPUBLIC, 1:00 -->
-
-    <timezone code="cf">Africa/Bangui</timezone>
-
-    <!-- CONGO, 1:00 -->
-
-    <timezone code="cg">Africa/Brazzaville</timezone>
-
-    <!-- SWITZERLAND, 1:00 -->
-
-    <timezone code="ch">Europe/Zurich</timezone>
-
-    <!-- COTE D'IVOIRE, 0:00 -->
-
-    <timezone code="ci">Africa/Abidjan</timezone>
-
-    <!-- COOK ISLANDS, -10:00 -->
-
-    <timezone code="ck">Pacific/Rarotonga</timezone>
-
-    <!-- CHILE, -3:00 -->
-
-    <timezone code="cl">America/Punta_Arenas</timezone>
-
-    <!-- CHILE, -4:00 -->
-
-    <timezone code="cl">America/Santiago</timezone>
-
-    <!-- CHILE, -6:00 -->
-
-    <timezone code="cl">Pacific/Easter</timezone>
-
-    <!-- CAMEROON, 1:00 -->
-
-    <timezone code="cm">Africa/Douala</timezone>
-
-    <!-- CHINA, 8:00 -->
-
-    <timezone code="cn">Asia/Shanghai</timezone>
-
-    <!-- CHINA, 6:00 -->
-
-    <timezone code="cn">Asia/Urumqi</timezone>
-
-    <!-- COLOMBIA, -5:00 -->
-
-    <timezone code="co">America/Bogota</timezone>
-
-    <!-- COSTA RICA, -6:00 -->
-
-    <timezone code="cr">America/Costa_Rica</timezone>
-
-    <!-- CUBA, -5:00 -->
-
-    <timezone code="cu">America/Havana</timezone>
-
-    <!-- CAPE VERDE, -1:00 -->
-
-    <timezone code="cv">Atlantic/Cape_Verde</timezone>
-
-    <!-- Curaçao, -4:00 -->
-
-    <timezone code="cw">America/Curacao</timezone>
-
-    <!-- CHRISTMAS ISLAND, 7:00 -->
-
-    <timezone code="cx">Indian/Christmas</timezone>
-
-    <!-- CYPRUS, 2:00 -->
-
-    <timezone code="cy">Asia/Nicosia</timezone>
-
-    <!-- CYPRUS, 3:00 -->
-
-    <timezone code="cy">Asia/Famagusta</timezone>
-
-    <!-- CZECH REPUBLIC, 1:00 -->
-
-    <timezone code="cz">Europe/Prague</timezone>
-
-    <!-- GERMANY, 1:00 -->
-
-    <timezone code="de">Europe/Berlin</timezone>
-    <timezone code="de">Europe/Busingen</timezone>
-
-    <!-- DJIBOUTI, 3:00 -->
-
-    <timezone code="dj">Africa/Djibouti</timezone>
-
-    <!-- DENMARK, 1:00 -->
-
-    <timezone code="dk">Europe/Copenhagen</timezone>
-
-    <!-- DOMINICA, -4:00 -->
-
-    <timezone code="dm">America/Dominica</timezone>
-
-    <!-- DOMINICAN REPUBLIC, -4:00 -->
-
-    <timezone code="do">America/Santo_Domingo</timezone>
-
-    <!-- ALGERIA, 1:00 -->
-
-    <timezone code="dz">Africa/Algiers</timezone>
-
-    <!-- ECUADOR, -5:00 -->
-
-    <timezone code="ec">America/Guayaquil</timezone>
-
-    <!-- ECUADOR, -6:00 -->
-
-    <timezone code="ec">Pacific/Galapagos</timezone>
-
-    <!-- ESTONIA, 2:00 -->
-
-    <timezone code="ee">Europe/Tallinn</timezone>
-
-    <!-- EGYPT, 2:00 -->
-
-    <timezone code="eg">Africa/Cairo</timezone>
-
-    <!-- WESTERN SAHARA, 0:00 -->
-
-    <timezone code="eh">Africa/El_Aaiun</timezone>
-
-    <!-- ERITREA, 3:00 -->
-
-    <timezone code="er">Africa/Asmara</timezone>
-
-    <!-- SPAIN, 1:00 -->
-
-    <timezone code="es">Europe/Madrid</timezone>
-    <timezone code="es">Africa/Ceuta</timezone>
-
-    <!-- SPAIN, 0:00 -->
-
-    <timezone code="es">Atlantic/Canary</timezone>
-
-    <!-- ETHIOPIA, 3:00 -->
-
-    <timezone code="et">Africa/Addis_Ababa</timezone>
-
-    <!-- FINLAND, 2:00 -->
-
-    <timezone code="fi">Europe/Helsinki</timezone>
-
-    <!-- FIJI, 12:00 -->
-
-    <timezone code="fj">Pacific/Fiji</timezone>
-
-    <!-- FALKLAND ISLANDS (MALVINAS), -3:00 -->
-
-    <timezone code="fk">Atlantic/Stanley</timezone>
-
-    <!-- MICRONESIA, FEDERATED STATES OF, 11:00 -->
-
-    <timezone code="fm">Pacific/Pohnpei</timezone>
-    <timezone code="fm">Pacific/Kosrae</timezone>
-
-    <!-- MICRONESIA, FEDERATED STATES OF, 10:00 -->
-
-    <timezone code="fm">Pacific/Chuuk</timezone>
-
-    <!-- FAROE ISLANDS, 0:00 -->
-
-    <timezone code="fo">Atlantic/Faroe</timezone>
-
-    <!-- FRANCE, 1:00 -->
-
-    <timezone code="fr">Europe/Paris</timezone>
-
-    <!-- GABON, 1:00 -->
-
-    <timezone code="ga">Africa/Libreville</timezone>
-
-    <!-- UNITED KINGDOM, 0:00 -->
-
-    <timezone code="gb">Europe/London</timezone>
-
-    <!-- GRENADA, -4:00 -->
-
-    <timezone code="gd">America/Grenada</timezone>
-
-    <!-- GEORGIA, 4:00 -->
-
-    <timezone code="ge">Asia/Tbilisi</timezone>
-
-    <!-- FRENCH GUIANA, -3:00 -->
-
-    <timezone code="gf">America/Cayenne</timezone>
-
-    <!-- GUERNSEY, 0:00 -->
-
-    <timezone code="gg">Europe/Guernsey</timezone>
-
-    <!-- GHANA, 0:00 -->
-
-    <timezone code="gh">Africa/Accra</timezone>
-
-    <!-- GIBRALTAR, 1:00 -->
-
-    <timezone code="gi">Europe/Gibraltar</timezone>
-
-    <!-- GREENLAND, 0:00 -->
-
-    <timezone code="gl">America/Danmarkshavn</timezone>
-
-    <!-- GREENLAND, -1:00 -->
-
-    <timezone code="gl">America/Scoresbysund</timezone>
-
-    <!-- GREENLAND, -3:00 -->
-
-    <timezone code="gl">America/Godthab</timezone>
-
-    <!-- GREENLAND, -4:00 -->
-
-    <timezone code="gl">America/Thule</timezone>
-
-    <!-- GAMBIA, 0:00 -->
-
-    <timezone code="gm">Africa/Banjul</timezone>
-
-    <!-- GUINEA, 0:00 -->
-
-    <timezone code="gn">Africa/Conakry</timezone>
-
-    <!-- GUADELOUPE, -4:00 -->
-
-    <timezone code="gp">America/Guadeloupe</timezone>
-
-    <!-- EQUATORIAL GUINEA, 1:00 -->
-
-    <timezone code="gq">Africa/Malabo</timezone>
-
-    <!-- GREECE, 2:00 -->
-
-    <timezone code="gr">Europe/Athens</timezone>
-
-    <!-- SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS, -2:00 -->
-
-    <timezone code="gs">Atlantic/South_Georgia</timezone>
-
-    <!-- GUATEMALA, -6:00 -->
-
-    <timezone code="gt">America/Guatemala</timezone>
-
-    <!-- GUAM, 10:00 -->
-
-    <timezone code="gu">Pacific/Guam</timezone>
-
-    <!-- GUINEA-BISSAU, 0:00 -->
-
-    <timezone code="gw">Africa/Bissau</timezone>
-
-    <!-- GUYANA, -4:00 -->
-
-    <timezone code="gy">America/Guyana</timezone>
-
-    <!-- HONG KONG, 8:00 -->
-
-    <timezone code="hk">Asia/Hong_Kong</timezone>
-
-    <!-- HONDURAS, -6:00 -->
-
-    <timezone code="hn">America/Tegucigalpa</timezone>
-
-    <!-- CROATIA, 1:00 -->
-
-    <timezone code="hr">Europe/Zagreb</timezone>
-
-    <!-- HAITI, -5:00 -->
-
-    <timezone code="ht">America/Port-au-Prince</timezone>
-
-    <!-- HUNGARY, 1:00 -->
-
-    <timezone code="hu">Europe/Budapest</timezone>
-
-    <!-- INDONESIA, 9:00 -->
-
-    <timezone code="id">Asia/Jayapura</timezone>
-
-    <!-- INDONESIA, 8:00 -->
-
-    <timezone code="id">Asia/Makassar</timezone>
-
-    <!-- INDONESIA, 7:00 -->
-
-    <timezone code="id">Asia/Jakarta</timezone>
-    <timezone code="id">Asia/Pontianak</timezone>
-
-    <!-- IRELAND, 0:00 -->
-
-    <timezone code="ie">Europe/Dublin</timezone>
-
-    <!-- ISRAEL, 2:00 -->
-
-    <timezone code="il">Asia/Jerusalem</timezone>
-
-    <!-- ISLE OF MAN, 0:00 -->
-
-    <timezone code="im">Europe/Isle_of_Man</timezone>
-
-    <!-- INDIA, 5:30 -->
-
-    <timezone code="in">Asia/Kolkata</timezone>
-
-    <!-- BRITISH INDIAN OCEAN TERRITORY, 6:00 -->
-
-    <timezone code="io">Indian/Chagos</timezone>
-
-    <!-- IRAQ, 3:00 -->
-
-    <timezone code="iq">Asia/Baghdad</timezone>
-
-    <!-- IRAN, ISLAMIC REPUBLIC OF, 3:30 -->
-
-    <timezone code="ir">Asia/Tehran</timezone>
-
-    <!-- ICELAND, 0:00 -->
-
-    <timezone code="is">Atlantic/Reykjavik</timezone>
-
-    <!-- ITALY, 1:00 -->
-
-    <timezone code="it">Europe/Rome</timezone>
-
-    <!-- JERSEY, 0:00 -->
-
-    <timezone code="je">Europe/Jersey</timezone>
-
-    <!-- JAMAICA, -5:00 -->
-
-    <timezone code="jm">America/Jamaica</timezone>
-
-    <!-- JORDAN, 2:00 -->
-
-    <timezone code="jo">Asia/Amman</timezone>
-
-    <!-- JAPAN, 9:00 -->
-
-    <timezone code="jp">Asia/Tokyo</timezone>
-
-    <!-- KENYA, 3:00 -->
-
-    <timezone code="ke">Africa/Nairobi</timezone>
-
-    <!-- KYRGYZSTAN, 6:00 -->
-
-    <timezone code="kg">Asia/Bishkek</timezone>
-
-    <!-- CAMBODIA, 7:00 -->
-
-    <timezone code="kh">Asia/Phnom_Penh</timezone>
-
-    <!-- KIRIBATI, 14:00 -->
-
-    <timezone code="ki">Pacific/Kiritimati</timezone>
-
-    <!-- KIRIBATI, 13:00 -->
-
-    <timezone code="ki">Pacific/Enderbury</timezone>
-
-    <!-- KIRIBATI, 12:00 -->
-
-    <timezone code="ki">Pacific/Tarawa</timezone>
-
-    <!-- COMOROS, 3:00 -->
-
-    <timezone code="km">Indian/Comoro</timezone>
-
-    <!-- SAINT KITTS AND NEVIS, -4:00 -->
-
-    <timezone code="kn">America/St_Kitts</timezone>
-
-    <!-- KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF, 8:30 -->
-
-    <timezone code="kp">Asia/Pyongyang</timezone>
-
-    <!-- KOREA, REPUBLIC OF, 9:00 -->
-
-    <timezone code="kr">Asia/Seoul</timezone>
-
-    <!-- KUWAIT, 3:00 -->
-
-    <timezone code="kw">Asia/Kuwait</timezone>
-
-    <!-- CAYMAN ISLANDS, -5:00 -->
-
-    <timezone code="ky">America/Cayman</timezone>
-
-    <!-- KAZAKHSTAN, 6:00 -->
-
-    <timezone code="kz">Asia/Almaty</timezone>
-    <timezone code="kz">Asia/Qyzylorda</timezone>
-
-    <!-- KAZAKHSTAN, 5:00 -->
-
-    <timezone code="kz">Asia/Aqtau</timezone>
-    <timezone code="kz">Asia/Oral</timezone>
-    <timezone code="kz">Asia/Aqtobe</timezone>
-    <timezone code="kz">Asia/Atyrau</timezone>
-
-    <!-- LAO PEOPLE'S DEMOCRATIC REPUBLIC, 7:00 -->
-
-    <timezone code="la">Asia/Vientiane</timezone>
-
-    <!-- LEBANON, 2:00 -->
-
-    <timezone code="lb">Asia/Beirut</timezone>
-
-    <!-- SAINT LUCIA, -4:00 -->
-
-    <timezone code="lc">America/St_Lucia</timezone>
-
-    <!-- LIECHTENSTEIN, 1:00 -->
-
-    <timezone code="li">Europe/Vaduz</timezone>
-
-    <!-- SRI LANKA, 5:30 -->
-
-    <timezone code="lk">Asia/Colombo</timezone>
-
-    <!-- LIBERIA, 0:00 -->
-
-    <timezone code="lr">Africa/Monrovia</timezone>
-
-    <!-- LESOTHO, 2:00 -->
-
-    <timezone code="ls">Africa/Maseru</timezone>
-
-    <!-- LITHUANIA, 2:00 -->
-
-    <timezone code="lt">Europe/Vilnius</timezone>
-
-    <!-- LUXEMBOURG, 1:00 -->
-
-    <timezone code="lu">Europe/Luxembourg</timezone>
-
-    <!-- LATVIA, 2:00 -->
-
-    <timezone code="lv">Europe/Riga</timezone>
-
-    <!-- LIBYAN ARAB JAMAHIRIYA, 2:00 -->
-
-    <timezone code="ly">Africa/Tripoli</timezone>
-
-    <!-- MOROCCO, 0:00 -->
-
-    <timezone code="ma">Africa/Casablanca</timezone>
-
-    <!-- MONACO, 1:00 -->
-
-    <timezone code="mc">Europe/Monaco</timezone>
-
-    <!-- MOLDOVA, 2:00 -->
-
-    <timezone code="md">Europe/Chisinau</timezone>
-
-    <!-- MONTENEGRO, 1:00 -->
-
-    <timezone code="me">Europe/Podgorica</timezone>
-
-    <!-- Collectivity of Saint Martin, -4:00 -->
-
-    <timezone code="mf">America/Marigot</timezone>
-
-    <!-- MADAGASCAR, 3:00 -->
-
-    <timezone code="mg">Indian/Antananarivo</timezone>
-
-    <!-- MARSHALL ISLANDS, 12:00 -->
-
-    <timezone code="mh">Pacific/Majuro</timezone>
-    <timezone code="mh">Pacific/Kwajalein</timezone>
-
-    <!-- MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF, 1:00 -->
-
-    <timezone code="mk">Europe/Skopje</timezone>
-
-    <!-- MALI, 0:00 -->
-
-    <timezone code="ml">Africa/Bamako</timezone>
-
-    <!-- MYANMAR, 6:30 -->
-
-    <timezone code="mm">Asia/Yangon</timezone>
-
-    <!-- MONGOLIA, 8:00 -->
-
-    <timezone code="mn">Asia/Choibalsan</timezone>
-    <timezone code="mn">Asia/Ulaanbaatar</timezone>
-
-    <!-- MONGOLIA, 7:00 -->
-
-    <timezone code="mn">Asia/Hovd</timezone>
-
-    <!-- MACAO, 8:00 -->
-
-    <timezone code="mo">Asia/Macau</timezone>
-
-    <!-- NORTHERN MARIANA ISLANDS, 10:00 -->
-
-    <timezone code="mp">Pacific/Saipan</timezone>
-
-    <!-- MARTINIQUE, -4:00 -->
-
-    <timezone code="mq">America/Martinique</timezone>
-
-    <!-- MAURITANIA, 0:00 -->
-
-    <timezone code="mr">Africa/Nouakchott</timezone>
-
-    <!-- MONTSERRAT, -4:00 -->
-
-    <timezone code="ms">America/Montserrat</timezone>
-
-    <!-- MALTA, 1:00 -->
-
-    <timezone code="mt">Europe/Malta</timezone>
-
-    <!-- MAURITIUS, 4:00 -->
-
-    <timezone code="mu">Indian/Mauritius</timezone>
-
-    <!-- MALDIVES, 5:00 -->
-
-    <timezone code="mv">Indian/Maldives</timezone>
-
-    <!-- MALAWI, 2:00 -->
-
-    <timezone code="mw">Africa/Blantyre</timezone>
-
-    <!-- MEXICO, -6:00 -->
-
-    <timezone code="mx">America/Mexico_City</timezone>
-    <timezone code="mx">America/Merida</timezone>
-    <timezone code="mx">America/Monterrey</timezone>
-    <timezone code="mx">America/Matamoros</timezone>
-    <timezone code="mx">America/Bahia_Banderas</timezone>
-
-    <!-- MEXICO, -5:00 -->
-
-    <timezone code="mx">America/Cancun</timezone>
-
-    <!-- MEXICO, -7:00 -->
-
-    <timezone code="mx">America/Chihuahua</timezone>
-    <timezone code="mx">America/Hermosillo</timezone>
-    <timezone code="mx">America/Mazatlan</timezone>
-    <timezone code="mx">America/Ojinaga</timezone>
-
-    <!-- MEXICO, -8:00 -->
-
-    <timezone code="mx">America/Tijuana</timezone>
-
-    <!-- MALAYSIA, 8:00 -->
-
-    <timezone code="my">Asia/Kuala_Lumpur</timezone>
-    <timezone code="my">Asia/Kuching</timezone>
-
-    <!-- MOZAMBIQUE, 2:00 -->
-
-    <timezone code="mz">Africa/Maputo</timezone>
-
-    <!-- NAMIBIA, 1:00 -->
-
-    <timezone code="na">Africa/Windhoek</timezone>
-
-    <!-- NEW CALEDONIA, 11:00 -->
-
-    <timezone code="nc">Pacific/Noumea</timezone>
-
-    <!-- NIGER, 1:00 -->
-
-    <timezone code="ne">Africa/Niamey</timezone>
-
-    <!-- NORFOLK ISLAND, 11:30 -->
-
-    <timezone code="nf">Pacific/Norfolk</timezone>
-
-    <!-- NIGERIA, 1:00 -->
-
-    <timezone code="ng">Africa/Lagos</timezone>
-
-    <!-- NICARAGUA, -6:00 -->
-
-    <timezone code="ni">America/Managua</timezone>
-
-    <!-- NETHERLANDS, 1:00 -->
-
-    <timezone code="nl">Europe/Amsterdam</timezone>
-
-    <!-- NORWAY, 1:00 -->
-
-    <timezone code="no">Europe/Oslo</timezone>
-
-    <!-- NEPAL, 5:45 -->
-
-    <timezone code="np">Asia/Kathmandu</timezone>
-
-    <!-- NAURU, 12:00 -->
-
-    <timezone code="nr">Pacific/Nauru</timezone>
-
-    <!-- NIUE, -11:00 -->
-
-    <timezone code="nu">Pacific/Niue</timezone>
-
-    <!-- NEW ZEALAND, 12:00 -->
-
-    <timezone code="nz">Pacific/Auckland</timezone>
-
-    <!-- NEW ZEALAND, 12:45 -->
-
-    <timezone code="nz">Pacific/Chatham</timezone>
-
-    <!-- OMAN, 4:00 -->
-
-    <timezone code="om">Asia/Muscat</timezone>
-
-    <!-- PANAMA, -5:00 -->
-
-    <timezone code="pa">America/Panama</timezone>
-
-    <!-- PERU, -5:00 -->
-
-    <timezone code="pe">America/Lima</timezone>
-
-    <!-- FRENCH POLYNESIA, -9:00 -->
-
-    <timezone code="pf">Pacific/Gambier</timezone>
-
-    <!-- FRENCH POLYNESIA, -9:30 -->
-
-    <timezone code="pf">Pacific/Marquesas</timezone>
-
-    <!-- FRENCH POLYNESIA, -10:00 -->
-
-    <timezone code="pf">Pacific/Tahiti</timezone>
-
-    <!-- PAPUA NEW GUINEA, 10:00 -->
-
-    <timezone code="pg">Pacific/Port_Moresby</timezone>
-
-    <!-- PAPUA NEW GUINEA, 11:00 -->
-
-    <timezone code="pg">Pacific/Bougainville</timezone>
-
-    <!-- PHILIPPINES, 8:00 -->
-
-    <timezone code="ph">Asia/Manila</timezone>
-
-    <!-- PAKISTAN, 5:00 -->
-
-    <timezone code="pk">Asia/Karachi</timezone>
-
-    <!-- POLAND, 1:00 -->
-
-    <timezone code="pl">Europe/Warsaw</timezone>
-
-    <!-- SAINT PIERRE AND MIQUELON, -3:00 -->
-
-    <timezone code="pm">America/Miquelon</timezone>
-
-    <!-- PITCAIRN, -8:00 -->
-
-    <timezone code="pn">Pacific/Pitcairn</timezone>
-
-    <!-- PUERTO RICO, -4:00 -->
-
-    <timezone code="pr">America/Puerto_Rico</timezone>
-
-    <!-- PALESTINE, 2:00 -->
-
-    <timezone code="ps">Asia/Gaza</timezone>
-    <timezone code="ps">Asia/Hebron</timezone>
-
-    <!-- PORTUGAL, 0:00 -->
-
-    <timezone code="pt">Europe/Lisbon</timezone>
-    <timezone code="pt">Atlantic/Madeira</timezone>
-
-    <!-- PORTUGAL, -1:00 -->
-
-    <timezone code="pt">Atlantic/Azores</timezone>
-
-    <!-- PALAU, 9:00 -->
-
-    <timezone code="pw">Pacific/Palau</timezone>
-
-    <!-- PARAGUAY, -4:00 -->
-
-    <timezone code="py">America/Asuncion</timezone>
-
-    <!-- QATAR, 3:00 -->
-
-    <timezone code="qa">Asia/Qatar</timezone>
-
-    <!-- REUNION, 4:00 -->
-
-    <timezone code="re">Indian/Reunion</timezone>
-
-    <!-- ROMANIA, 2:00 -->
-
-    <timezone code="ro">Europe/Bucharest</timezone>
-
-    <!-- SERBIA, 1:00 -->
-
-    <timezone code="rs">Europe/Belgrade</timezone>
-
-    <!-- RUSSIAN FEDERATION, 12:00 -->
-
-    <timezone code="ru">Asia/Kamchatka</timezone>
-    <timezone code="ru">Asia/Anadyr</timezone>
-
-    <!-- RUSSIAN FEDERATION, 11:00 -->
-
-    <timezone code="ru">Asia/Magadan</timezone>
-    <timezone code="ru">Asia/Sakhalin</timezone>
-    <timezone code="ru">Asia/Srednekolymsk</timezone>
-
-    <!-- RUSSIAN FEDERATION, 10:00 -->
-
-    <timezone code="ru">Asia/Vladivostok</timezone>
-    <timezone code="ru">Asia/Ust-Nera</timezone>
-
-    <!-- RUSSIAN FEDERATION, 9:00 -->
-
-    <timezone code="ru">Asia/Yakutsk</timezone>
-    <timezone code="ru">Asia/Chita</timezone>
-    <timezone code="ru">Asia/Khandyga</timezone>
-
-    <!-- RUSSIAN FEDERATION, 8:00 -->
-
-    <timezone code="ru">Asia/Irkutsk</timezone>
-
-    <!-- RUSSIAN FEDERATION, 7:00 -->
-
-    <timezone code="ru">Asia/Krasnoyarsk</timezone>
-    <timezone code="ru">Asia/Novosibirsk</timezone>
-    <timezone code="ru">Asia/Barnaul</timezone>
-    <timezone code="ru">Asia/Novokuznetsk</timezone>
-    <timezone code="ru">Asia/Tomsk</timezone>
-
-    <!-- RUSSIAN FEDERATION, 6:00 -->
-
-    <timezone code="ru">Asia/Omsk</timezone>
-
-    <!-- RUSSIAN FEDERATION, 5:00 -->
-
-    <timezone code="ru">Asia/Yekaterinburg</timezone>
-
-    <!-- RUSSIAN FEDERATION, 4:00 -->
-
-    <timezone code="ru">Europe/Samara</timezone>
-    <timezone code="ru">Europe/Astrakhan</timezone>
-    <timezone code="ru">Europe/Ulyanovsk</timezone>
-    <timezone code="ru">Europe/Saratov</timezone>
-
-    <!-- RUSSIAN FEDERATION, 3:00 -->
-
-    <timezone code="ru">Europe/Moscow</timezone>
-    <timezone code="ru">Europe/Volgograd</timezone>
-    <timezone code="ru">Europe/Kirov</timezone>
-    <timezone code="ru">Europe/Simferopol</timezone>
-
-    <!-- RUSSIAN FEDERATION, 2:00 -->
-
-    <timezone code="ru">Europe/Kaliningrad</timezone>
-
-    <!-- RWANDA, 2:00 -->
-
-    <timezone code="rw">Africa/Kigali</timezone>
-
-    <!-- SAUDI ARABIA, 3:00 -->
-
-    <timezone code="sa">Asia/Riyadh</timezone>
-
-    <!-- SOLOMON ISLANDS, 11:00 -->
-
-    <timezone code="sb">Pacific/Guadalcanal</timezone>
-
-    <!-- SEYCHELLES, 4:00 -->
-
-    <timezone code="sc">Indian/Mahe</timezone>
-
-    <!-- SUDAN, 3:00 -->
-
-    <timezone code="sd">Africa/Khartoum</timezone>
-
-    <!-- SWEDEN, 1:00 -->
-
-    <timezone code="se">Europe/Stockholm</timezone>
-
-    <!-- SINGAPORE, 8:00 -->
-
-    <timezone code="sg">Asia/Singapore</timezone>
-
-    <!-- SAINT HELENA, 0:00 -->
-
-    <timezone code="sh">Atlantic/St_Helena</timezone>
-
-    <!-- SLOVENIA, 1:00 -->
-
-    <timezone code="si">Europe/Ljubljana</timezone>
-
-    <!-- SVALBARD AND JAN MAYEN, 1:00 -->
-
-    <timezone code="sj">Arctic/Longyearbyen</timezone>
-
-    <!-- SLOVAKIA, 1:00 -->
-
-    <timezone code="sk">Europe/Bratislava</timezone>
-
-    <!-- SIERRA LEONE, 0:00 -->
-
-    <timezone code="sl">Africa/Freetown</timezone>
-
-    <!-- SAN MARINO, 1:00 -->
-
-    <timezone code="sm">Europe/San_Marino</timezone>
-
-    <!-- SENEGAL, 0:00 -->
-
-    <timezone code="sn">Africa/Dakar</timezone>
-
-    <!-- SOMALIA, 3:00 -->
-
-    <timezone code="so">Africa/Mogadishu</timezone>
-
-    <!-- SURINAME, -3:00 -->
-
-    <timezone code="sr">America/Paramaribo</timezone>
-
-    <!-- South Sudan, 3:00 -->
-
-    <timezone code="ss">Africa/Juba</timezone>
-
-    <!-- SAO TOME AND PRINCIPE, 0:00 -->
-
-    <timezone code="st">Africa/Sao_Tome</timezone>
-
-    <!-- EL SALVADOR, -6:00 -->
-
-    <timezone code="sv">America/El_Salvador</timezone>
-
-    <!-- Sint Maarten, -4:00 -->
-
-    <timezone code="sx">America/Lower_Princes</timezone>
-
-    <!-- SYRIAN ARAB REPUBLIC, 2:00 -->
-
-    <timezone code="sy">Asia/Damascus</timezone>
-
-    <!-- SWAZILAND, 2:00 -->
-
-    <timezone code="sz">Africa/Mbabane</timezone>
-
-    <!-- TURKS AND CAICOS ISLANDS, -4:00 -->
-
-    <timezone code="tc">America/Grand_Turk</timezone>
-
-    <!-- CHAD, 1:00 -->
-
-    <timezone code="td">Africa/Ndjamena</timezone>
-
-    <!-- FRENCH SOUTHERN TERRITORIES, 5:00 -->
-
-    <timezone code="tf">Indian/Kerguelen</timezone>
-
-    <!-- TOGO, 0:00 -->
-
-    <timezone code="tg">Africa/Lome</timezone>
-
-    <!-- THAILAND, 7:00 -->
-
-    <timezone code="th">Asia/Bangkok</timezone>
-
-    <!-- TAJIKISTAN, 5:00 -->
-
-    <timezone code="tj">Asia/Dushanbe</timezone>
-
-    <!-- TOKELAU, +13:00 -->
-
-    <timezone code="tk">Pacific/Fakaofo</timezone>
-
-    <!-- TIMOR-LESTE, 9:00 -->
-
-    <timezone code="tl">Asia/Dili</timezone>
-
-    <!-- TURKMENISTAN, 5:00 -->
-
-    <timezone code="tm">Asia/Ashgabat</timezone>
-
-    <!-- TUNISIA, 1:00 -->
-
-    <timezone code="tn">Africa/Tunis</timezone>
-
-    <!-- TONGA, 13:00 -->
-
-    <timezone code="to">Pacific/Tongatapu</timezone>
-
-    <!-- TURKEY, 3:00 -->
-
-    <timezone code="tr">Europe/Istanbul</timezone>
-
-    <!-- TRINIDAD AND TOBAGO, -4:00 -->
-
-    <timezone code="tt">America/Port_of_Spain</timezone>
-
-    <!-- TUVALU, 12:00 -->
-
-    <timezone code="tv">Pacific/Funafuti</timezone>
-
-    <!-- TAIWAN, PROVINCE OF CHINA, 8:00 -->
-
-    <timezone code="tw">Asia/Taipei</timezone>
-
-    <!-- TANZANIA, UNITED REPUBLIC OF, 3:00 -->
-
-    <timezone code="tz">Africa/Dar_es_Salaam</timezone>
-
-    <!-- UKRAINE, 2:00 -->
-
-    <timezone code="ua">Europe/Kiev</timezone>
-    <timezone code="ua">Europe/Uzhgorod</timezone>
-    <timezone code="ua">Europe/Zaporozhye</timezone>
-
-    <!-- UGANDA, 3:00 -->
-
-    <timezone code="ug">Africa/Kampala</timezone>
-
-    <!-- UNITED STATES MINOR OUTLYING ISLANDS, 12:00 -->
-
-    <timezone code="um">Pacific/Wake</timezone>
-
-    <!-- UNITED STATES MINOR OUTLYING ISLANDS, -11:00 -->
-
-    <timezone code="um">Pacific/Midway</timezone>
-
-    <!-- UNITED STATES, -5:00 -->
-
-    <timezone code="us">America/New_York</timezone>
-    <timezone code="us">America/Detroit</timezone>
-    <timezone code="us">America/Kentucky/Louisville</timezone>
-    <timezone code="us">America/Kentucky/Monticello</timezone>
-    <timezone code="us">America/Indiana/Indianapolis</timezone>
-    <timezone code="us">America/Indiana/Vincennes</timezone>
-    <timezone code="us">America/Indiana/Winamac</timezone>
-    <timezone code="us">America/Indiana/Marengo</timezone>
-    <timezone code="us">America/Indiana/Petersburg</timezone>
-    <timezone code="us">America/Indiana/Vevay</timezone>
-
-    <!-- UNITED STATES, -6:00 -->
-
-    <timezone code="us">America/Chicago</timezone>
-    <timezone code="us">America/Indiana/Knox</timezone>
-    <timezone code="us">America/Menominee</timezone>
-    <timezone code="us">America/North_Dakota/Center</timezone>
-    <timezone code="us">America/North_Dakota/New_Salem</timezone>
-    <timezone code="us">America/Indiana/Tell_City</timezone>
-    <timezone code="us">America/North_Dakota/Beulah</timezone>
-
-    <!-- UNITED STATES, -7:00 -->
-
-    <timezone code="us">America/Denver</timezone>
-    <timezone code="us">America/Boise</timezone>
-    <timezone code="us">America/Phoenix</timezone>
-
-    <!-- UNITED STATES, -8:00 -->
-
-    <timezone code="us">America/Los_Angeles</timezone>
-
-    <!-- UNITED STATES, -9:00 -->
-
-    <timezone code="us">America/Anchorage</timezone>
-    <timezone code="us">America/Juneau</timezone>
-    <timezone code="us">America/Yakutat</timezone>
-    <timezone code="us">America/Nome</timezone>
-    <timezone code="us">America/Metlakatla</timezone>
-    <timezone code="us">America/Sitka</timezone>
-
-    <!-- UNITED STATES, -10:00 -->
-
-    <timezone code="us">Pacific/Honolulu</timezone>
-    <timezone code="us">America/Adak</timezone>
-
-    <!-- URUGUAY, -3:00 -->
-
-    <timezone code="uy">America/Montevideo</timezone>
-
-    <!-- UZBEKISTAN, 5:00 -->
-
-    <timezone code="uz">Asia/Tashkent</timezone>
-    <timezone code="uz">Asia/Samarkand</timezone>
-
-    <!-- HOLY SEE (VATICAN CITY STATE), 1:00 -->
-
-    <timezone code="va">Europe/Vatican</timezone>
-
-    <!-- SAINT VINCENT AND THE GRENADINES, -4:00 -->
-
-    <timezone code="vc">America/St_Vincent</timezone>
-
-    <!-- VENEZUELA, -4:00 -->
-
-    <timezone code="ve">America/Caracas</timezone>
-
-    <!-- VIRGIN ISLANDS, BRITISH, -4:00 -->
-
-    <timezone code="vg">America/Tortola</timezone>
-
-    <!-- VIRGIN ISLANDS, U.S., -4:00 -->
-
-    <timezone code="vi">America/St_Thomas</timezone>
-
-    <!-- VIET NAM, 7:00 -->
-
-    <timezone code="vn">Asia/Ho_Chi_Minh</timezone>
-
-    <!-- VANUATU, 11:00 -->
-
-    <timezone code="vu">Pacific/Efate</timezone>
-
-    <!-- WALLIS AND FUTUNA, 12:00 -->
-
-    <timezone code="wf">Pacific/Wallis</timezone>
-
-    <!-- SAMOA, 13:00 -->
-
-    <timezone code="ws">Pacific/Apia</timezone>
-
-    <!-- YEMEN, 3:00 -->
-
-    <timezone code="ye">Asia/Aden</timezone>
-
-    <!-- MAYOTTE, 3:00 -->
-
-    <timezone code="yt">Indian/Mayotte</timezone>
-
-    <!-- SOUTH AFRICA, 2:00 -->
-
-    <timezone code="za">Africa/Johannesburg</timezone>
-
-    <!-- ZAMBIA, 2:00 -->
-
-    <timezone code="zm">Africa/Lusaka</timezone>
-
-    <!-- ZIMBABWE, 2:00 -->
-
-    <timezone code="zw">Africa/Harare</timezone>
-</timezones>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a999483..279cca7 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -52,6 +52,7 @@
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.annotation.MainThread;
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.Notification;
@@ -126,6 +127,8 @@
  * <li>Stops itself if it doesn't have any process left to monitor.
  * </ol>
  * </ol>
+ *
+ * TODO: There are multiple threads involved.  Add synchronization accordingly.
  */
 public class BugreportProgressService extends Service {
     private static final String TAG = "BugreportProgressService";
@@ -201,11 +204,15 @@
 
     private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
 
+    private final Object mLock = new Object();
+
     /** Managed dumpstate processes (keyed by id) */
     private final SparseArray<DumpstateListener> mProcesses = new SparseArray<>();
 
     private Context mContext;
-    private ServiceHandler mMainHandler;
+
+    private Handler mMainThreadHandler;
+    private ServiceHandler mServiceHandler;
     private ScreenshotHandler mScreenshotHandler;
 
     private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog();
@@ -234,7 +241,8 @@
     @Override
     public void onCreate() {
         mContext = getApplicationContext();
-        mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread");
+        mMainThreadHandler = new Handler(Looper.getMainLooper());
+        mServiceHandler = new ServiceHandler("BugreportProgressServiceMainThread");
         mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
 
         mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
@@ -260,10 +268,10 @@
         Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
         if (intent != null) {
             // Handle it in a separate thread.
-            final Message msg = mMainHandler.obtainMessage();
+            final Message msg = mServiceHandler.obtainMessage();
             msg.what = MSG_SERVICE_COMMAND;
             msg.obj = intent;
-            mMainHandler.sendMessage(msg);
+            mServiceHandler.sendMessage(msg);
         }
 
         // If service is killed it cannot be recreated because it would not know which
@@ -278,7 +286,7 @@
 
     @Override
     public void onDestroy() {
-        mMainHandler.getLooper().quit();
+        mServiceHandler.getLooper().quit();
         mScreenshotHandler.getLooper().quit();
         super.onDestroy();
     }
@@ -613,7 +621,7 @@
             // ignore it
         }
 
-        mInfoDialog.initialize(mContext, info);
+        mMainThreadHandler.post(() -> mInfoDialog.initialize(mContext, info));
     }
 
     /**
@@ -652,11 +660,11 @@
     private void takeScreenshot(int id, int delay) {
         if (delay > 0) {
             Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
-            final Message msg = mMainHandler.obtainMessage();
+            final Message msg = mServiceHandler.obtainMessage();
             msg.what = MSG_DELAYED_SCREENSHOT;
             msg.arg1 = id;
             msg.arg2 = delay - 1;
-            mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
+            mServiceHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
             return;
         }
 
@@ -696,7 +704,7 @@
         boolean taken = takeScreenshot(mContext, screenshotFile);
         setTakingScreenshot(false);
 
-        Message.obtain(mMainHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
+        Message.obtain(mServiceHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
                 screenshotFile).sendToTarget();
     }
 
@@ -1103,6 +1111,12 @@
      * description will be saved on {@code description.txt}.
      */
     private void addDetailsToZipFile(BugreportInfo info) {
+        synchronized (mLock) {
+            addDetailsToZipFileLocked(info);
+        }
+    }
+
+    private void addDetailsToZipFileLocked(BugreportInfo info) {
         if (info.bugreportFile == null) {
             // One possible reason is a bug in the Parcelization code.
             Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
@@ -1424,6 +1438,7 @@
         /**
          * Sets its internal state and displays the dialog.
          */
+        @MainThread
         void initialize(final Context context, BugreportInfo info) {
             final String dialogTitle =
                     context.getString(R.string.bugreport_info_dialog_title, info.id);
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index e3063c5..143a462 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -88,4 +88,6 @@
         android:contentDescription="@string/accessibility_unlock_button"
         android:scaleType="center" />
 
+    <include layout="@layout/keyguard_bottom_area_overlay" />
+
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area_overlay.xml b/packages/SystemUI/res/layout/keyguard_bottom_area_overlay.xml
new file mode 100644
index 0000000..37ba35f
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- empty stub -->
+<merge />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index fb47bbc..fb4ac04 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -35,6 +35,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:elevation="4dp"
+        android:background="#00000000"
         android:layout_marginBottom="48dp" />
 
     <include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 65344b7..2502d41 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,6 +24,7 @@
     android:layout_height="@dimen/status_bar_header_height"
     android:layout_gravity="@integer/notification_panel_layout_gravity"
     android:baselineAligned="false"
+    android:background="#00000000"
     android:elevation="4dp"
     android:clickable="false"
     android:clipChildren="false"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e154462..7c0aa07 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -52,7 +52,6 @@
 import android.os.IRemoteCallback;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -62,7 +61,6 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -74,6 +72,8 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -131,6 +131,7 @@
     private static final int MSG_SCREEN_TURNED_OFF = 332;
     private static final int MSG_DREAMING_STATE_CHANGED = 333;
     private static final int MSG_USER_UNLOCKED = 334;
+    private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
 
     /** Fingerprint state: Not listening to fingerprint. */
     private static final int FINGERPRINT_STATE_STOPPED = 0;
@@ -170,6 +171,8 @@
     private boolean mBootCompleted;
     private boolean mNeedsSlowUnlockTransition;
     private boolean mHasLockscreenWallpaper;
+    private boolean mAssistantVisible;
+    private boolean mKeyguardOccluded;
 
     // Device provisioning state
     private boolean mDeviceProvisioned;
@@ -287,6 +290,10 @@
                 case MSG_USER_UNLOCKED:
                     handleUserUnlocked();
                     break;
+                case MSG_ASSISTANT_STACK_CHANGED:
+                    mAssistantVisible = (boolean)msg.obj;
+                    updateFingerprintListeningState();
+                    break;
             }
         }
     };
@@ -414,6 +421,15 @@
         mKeyguardGoingAway = goingAway;
     }
 
+    /**
+     * Updates KeyguardUpdateMonitor's internal state to know if keyguard is occluded
+     * @param occluded
+     */
+    public void setKeyguardOccluded(boolean occluded) {
+        mKeyguardOccluded = occluded;
+        updateFingerprintListeningState();
+    }
+
     private void onFingerprintAuthenticated(int userId) {
         Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
         mUserFingerprintAuthenticated.put(userId, true);
@@ -429,6 +445,11 @@
                 cb.onFingerprintAuthenticated(userId);
             }
         }
+
+        // Only authenticate fingerprint once when assistant is visible
+        mAssistantVisible = false;
+        updateFingerprintListeningState();
+
         Trace.endSection();
     }
 
@@ -1110,6 +1131,7 @@
             mFpm.addLockoutResetCallback(mLockoutResetCallback);
         }
 
+        SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
         mUserManager = context.getSystemService(UserManager.class);
     }
 
@@ -1126,7 +1148,8 @@
 
     private boolean shouldListenForFingerprint() {
         return (mKeyguardIsVisible || !mDeviceInteractive ||
-                    (mBouncer && !mKeyguardGoingAway) || mGoingToSleep)
+                    (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
+                    (mAssistantVisible && mKeyguardOccluded))
                 && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser());
     }
 
@@ -1155,8 +1178,10 @@
     private void stopListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
         if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
-            mFingerprintCancelSignal.cancel();
-            mFingerprintCancelSignal = null;
+            if (mFingerprintCancelSignal != null) {
+                mFingerprintCancelSignal.cancel();
+                mFingerprintCancelSignal = null;
+            }
             setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
         }
         if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
@@ -1679,6 +1704,23 @@
         }
     }
 
+    private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+        @Override
+        public void onTaskStackChangedBackground() {
+            try {
+                ActivityManager.StackInfo info = ActivityManager.getService().getStackInfo(
+                        ActivityManager.StackId.ASSISTANT_STACK_ID);
+                if (info == null) {
+                    return;
+                }
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
+                        info.visible));
+            } catch (RemoteException e) {
+                Log.e(TAG, "unable to check task stack", e);
+            }
+        }
+    };
+
     /**
      * @return true if and only if the state has changed for the specified {@code slotId}
      */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b977dd4..a22d8994 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1173,6 +1173,7 @@
 
             if (mOccluded != isOccluded) {
                 mOccluded = isOccluded;
+                mUpdateMonitor.setKeyguardOccluded(isOccluded);
                 mStatusBarKeyguardViewManager.setOccluded(isOccluded, animate
                         && mDeviceInteractive);
                 adjustStatusBarLocked();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 2cb6c08..a1160de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -827,10 +827,10 @@
     public void onDensityOrFontScaleChanged() {
         super.onDensityOrFontScaleChanged();
         initDimens();
-        if (mIsSummaryWithChildren) {
-            if (mChildrenContainer != null) {
-                mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
-            }
+        // Let's update our childrencontainer. This is intentionally not guarded with
+        // mIsSummaryWithChildren since we might have had children but not anymore.
+        if (mChildrenContainer != null) {
+            mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
         }
         if (mGuts != null) {
             View oldGuts = mGuts;
@@ -1496,9 +1496,11 @@
     public void setUserLocked(boolean userLocked) {
         mUserLocked = userLocked;
         mPrivateLayout.setUserExpanding(userLocked);
-        if (mIsSummaryWithChildren) {
+        // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
+        // children but not anymore.
+        if (mChildrenContainer != null) {
             mChildrenContainer.setUserLocked(userLocked);
-            if (userLocked || !isGroupExpanded()) {
+            if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
                 updateBackgroundForGroupState();
             }
         }
@@ -2228,4 +2230,9 @@
             }
         }
     }
+
+    @VisibleForTesting
+    protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
+        mChildrenContainer = childrenContainer;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index c5f23c5..6b276f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -53,6 +53,7 @@
     private float mInFrontTarget;
     private float mBehindTarget;
     private boolean mDozingAborted;
+    private boolean mWakeAndUnlocking;
 
     public DozeScrimController(ScrimController scrimController, Context context) {
         mContext = context;
@@ -63,6 +64,7 @@
     public void setDozing(boolean dozing, boolean animate) {
         if (mDozing == dozing) return;
         mDozing = dozing;
+        mWakeAndUnlocking = false;
         if (mDozing) {
             mDozingAborted = false;
             abortAnimations();
@@ -85,6 +87,16 @@
         }
     }
 
+    public void setWakeAndUnlocking() {
+        // Immediately abort the doze scrims in case of wake-and-unlock
+        // for pulsing so the Keyguard fade-out animation scrim can take over.
+        if (!mWakeAndUnlocking) {
+            mWakeAndUnlocking = true;
+            mScrimController.setDozeBehindAlpha(0f);
+            mScrimController.setDozeInFrontAlpha(0f);
+        }
+    }
+
     /** When dozing, fade screen contents in and out using the front scrim. */
     public void pulse(@NonNull DozeHost.PulseCallback callback, int reason) {
         if (callback == null) {
@@ -109,7 +121,7 @@
      */
     public void abortPulsing() {
         cancelPulsing();
-        if (mDozing) {
+        if (mDozing && !mWakeAndUnlocking) {
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(
                     mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
@@ -244,6 +256,9 @@
     }
 
     private void setDozeAlpha(boolean inFront, float alpha) {
+        if (mWakeAndUnlocking) {
+            return;
+        }
         if (inFront) {
             mScrimController.setDozeInFrontAlpha(alpha);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index f216d6c..6cb722f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -98,7 +98,6 @@
     private StatusBar mStatusBar;
     private final UnlockMethodCache mUnlockMethodCache;
     private final Context mContext;
-    private boolean mGoingToSleep;
     private int mPendingAuthenticatedUserId = -1;
 
     public FingerprintUnlockController(Context context,
@@ -213,17 +212,19 @@
                 Trace.endSection();
                 break;
             case MODE_WAKE_AND_UNLOCK_PULSING:
-                Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
-                mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
-                        true /* allowEnterAnimation */);
-                // Fall through.
-                Trace.endSection();
             case MODE_WAKE_AND_UNLOCK:
-                Trace.beginSection("MODE_WAKE_AND_UNLOCK");
+                if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
+                    Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
+                    mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
+                            true /* allowEnterAnimation */);
+                } else {
+                    Trace.beginSection("MODE_WAKE_AND_UNLOCK");
+                    mDozeScrimController.abortDoze();
+                }
                 mStatusBarWindowManager.setStatusBarFocusable(false);
-                mDozeScrimController.abortDoze();
                 mKeyguardViewMediator.onWakeAndUnlocking();
                 mScrimController.setWakeAndUnlocking();
+                mDozeScrimController.setWakeAndUnlocking();
                 if (mStatusBar.getNavigationBarView() != null) {
                     mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
                 }
@@ -302,10 +303,7 @@
     }
 
     private void cleanup() {
-        mMode = MODE_NONE;
         releaseFingerprintWakeLock();
-        mStatusBarWindowManager.setForceDozeBrightness(false);
-        mStatusBar.notifyFpAuthModeChanged();
     }
 
     public void startKeyguardFadingAway() {
@@ -321,6 +319,7 @@
 
     public void finishKeyguardFadingAway() {
         mMode = MODE_NONE;
+        mStatusBarWindowManager.setForceDozeBrightness(false);
         if (mStatusBar.getNavigationBarView() != null) {
             mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 290119b0..7907c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2418,17 +2418,26 @@
     @Override
     public void setAlpha(float alpha) {
         super.setAlpha(alpha);
-        updateFullyVisibleState();
+        updateFullyVisibleState(false /* forceNotFullyVisible */);
+    }
+
+    /**
+     * Must be called before starting a ViewPropertyAnimator alpha animation because those
+     * do NOT call setAlpha and therefore don't properly update the fullyVisibleState.
+     */
+    public void notifyStartFading() {
+        updateFullyVisibleState(true /* forceNotFullyVisible */);
     }
 
     @Override
     public void setVisibility(int visibility) {
         super.setVisibility(visibility);
-        updateFullyVisibleState();
+        updateFullyVisibleState(false /* forceNotFullyVisible */);
     }
 
-    private void updateFullyVisibleState() {
-        mNotificationStackScroller.setParentNotFullyVisible(getAlpha() != 1.0f
+    private void updateFullyVisibleState(boolean forceNotFullyVisible) {
+        mNotificationStackScroller.setParentNotFullyVisible(forceNotFullyVisible
+                || getAlpha() != 1.0f
                 || getVisibility() != VISIBLE);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 01bab3f..70f888f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4192,6 +4192,7 @@
      * fading.
      */
     public void fadeKeyguardWhilePulsing() {
+        mNotificationPanel.notifyStartFading();
         mNotificationPanel.animate()
                 .alpha(0f)
                 .setStartDelay(0)
@@ -4411,12 +4412,7 @@
         mKeyguardIndicationController.setDozing(mDozing);
         mNotificationPanel.setDark(mDozing, animate);
         updateQsExpansionEnabled();
-
-        // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
-        // for pulsing so the Keyguard fade-out animation scrim can take over.
-        mDozeScrimController.setDozing(mDozing &&
-                mFingerprintUnlockController.getMode()
-                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+        mDozeScrimController.setDozing(mDozing, animate);
         updateRowStates();
         Trace.endSection();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 913e6af..5c3c43b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -403,6 +403,7 @@
                 } else {
                     mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
                     mStatusBar.finishKeyguardFadingAway();
+                    mFingerprintUnlockController.finishKeyguardFadingAway();
                 }
             }
             mStatusBarWindowManager.setKeyguardShowing(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e1acc9b..e0f4429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -18,6 +18,7 @@
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
+import android.R.attr;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.Notification;
@@ -31,6 +32,7 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -52,6 +54,7 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
@@ -739,7 +742,12 @@
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
             }
-            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
+            Drawable icon = UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ false);
+            if (item.isGuest) {
+                icon.setColorFilter(Utils.getColorAttr(context, android.R.attr.colorForeground),
+                        Mode.SRC_IN);
+            }
+            return icon;
         }
 
         public void refresh() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5069b91..2081561 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -29,6 +29,8 @@
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
 import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -1243,4 +1245,9 @@
         }
         return getGroupExpandFraction();
     }
+
+    @VisibleForTesting
+    public boolean isUserLocked() {
+        return mUserLocked;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 183d8d9..6286301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
@@ -26,6 +28,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Assert;
@@ -65,4 +68,25 @@
                 == View.VISIBLE);
     }
 
+    @Test
+    public void testUserLockedResetEvenWhenNoChildren() {
+        mGroup.setUserLocked(true);
+        mGroup.removeAllChildren();
+        mGroup.setUserLocked(false);
+        Assert.assertFalse("The childrencontainer should not be userlocked but is, the state "
+                + "seems out of sync.", mGroup.getChildrenContainer().isUserLocked());
+    }
+
+    @Test
+    public void testReinflatedOnDensityChange() {
+        mGroup.setUserLocked(true);
+        mGroup.removeAllChildren();
+        mGroup.setUserLocked(false);
+        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+        mGroup.setChildrenContainer(mockContainer);
+        mGroup.onDensityOrFontScaleChanged();
+        verify(mockContainer).reInflateViews(any(), any());
+    }
+
+
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index edef16d..d8d91af 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4005,6 +4005,26 @@
     // FIELD: The numeric preference value (of type float) when it is changed in Settings
     FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE = 995;
 
+    // OPEN: Settings > System > Languages & input > Assist gesture
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    SETTINGS_ASSIST_GESTURE = 996;
+
+    // ACTION: Assist gesture released without triggering
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O DR
+    ASSIST_GESTURE_RELEASED = 997;
+
+    // ACTION: Assist gesture primed
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O DR
+    ASSIST_GESTURE_PRIMED = 998;
+
+    // ACTION: Assist gesture triggered
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O DR
+    ASSIST_GESTURE_TRIGGERED = 999;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index aa80075..bad8dcf 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -174,13 +174,13 @@
         public void send(int resultCode, Bundle resultData) throws RemoteException {
             final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
             if (structure == null) {
-                Slog.wtf(TAG, "no assist structure");
+                Slog.e(TAG, "No assist structure - app might have crashed providing it");
                 return;
             }
 
             final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS);
             if (receiverExtras == null) {
-                Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver");
+                Slog.e(TAG, "No receiver extras - app might have crashed providing it");
                 return;
             }
 
@@ -682,7 +682,6 @@
                 removeSelf();
                 return;
             }
-            resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH);
         }
 
         final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
@@ -1362,7 +1361,6 @@
             }
 
             // ...or handle authentication.
-            // TODO(b/37424539): proper implementation
             mService.setDatasetAuthenticationSelected(dataset.getId());
             setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
             final Intent fillInIntent = createAuthFillInIntent(
@@ -1455,21 +1453,36 @@
             }
             try {
                 if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+
                 // Skip null values as a null values means no change
                 final int entryCount = dataset.getFieldIds().size();
                 final List<AutofillId> ids = new ArrayList<>(entryCount);
                 final List<AutofillValue> values = new ArrayList<>(entryCount);
+                boolean waitingDatasetAuth = false;
                 for (int i = 0; i < entryCount; i++) {
                     if (dataset.getFieldValues().get(i) == null) {
                         continue;
                     }
-                    ids.add(dataset.getFieldIds().get(i));
+                    final AutofillId viewId = dataset.getFieldIds().get(i);
+                    ids.add(viewId);
                     values.add(dataset.getFieldValues().get(i));
+                    final ViewState viewState = mViewStates.get(viewId);
+                    if (viewState != null
+                            && (viewState.getState() & ViewState.STATE_WAITING_DATASET_AUTH) != 0) {
+                        if (sVerbose) {
+                            Slog.v(TAG, "autofillApp(): view " + viewId + " waiting auth");
+                        }
+                        waitingDatasetAuth = true;
+                        viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH);
+                    }
                 }
                 if (!ids.isEmpty()) {
+                    if (waitingDatasetAuth) {
+                        hideFillUiIfOwnedByMe();
+                    }
                     mClient.autofill(id, ids, values);
+                    setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, false);
                 }
-                setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, false);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error autofilling activity: " + e);
             }
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index f87fa19..8a52c96 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -174,7 +174,7 @@
      * fill UI is ready to be displayed (i.e. when response and bounds are set).
      */
     void maybeCallOnFillReady() {
-        if ((mState & (STATE_AUTOFILLED | STATE_WAITING_DATASET_AUTH)) != 0) {
+        if ((mState & STATE_AUTOFILLED) != 0) {
             if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString());
             return;
         }
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9fa6624..03e9dd2 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -222,7 +222,9 @@
         long[] clickEffectTimings = getLongIntArray(context.getResources(),
                 com.android.internal.R.array.config_virtualKeyVibePattern);
         VibrationEffect clickEffect;
-        if (clickEffectTimings.length == 1) {
+        if (clickEffectTimings.length == 0) {
+            clickEffect = null;
+        } else if (clickEffectTimings.length == 1) {
             clickEffect = VibrationEffect.createOneShot(
                     clickEffectTimings[0], VibrationEffect.DEFAULT_AMPLITUDE);
         } else {
@@ -232,7 +234,6 @@
                 new long[] {0, 30, 100, 30} /*timings*/, -1);
 
         mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect };
-
     }
 
     public void systemReady() {
@@ -701,7 +702,7 @@
                 }
             }
             final int id = prebaked.getId();
-            if (id < 0 || id >= mFallbackEffects.length) {
+            if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
                 return 0;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6265868..2d340d0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -90,7 +90,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
@@ -114,7 +113,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.os.Vibrator;
 import android.os.VibrationEffect;
 import android.provider.Settings;
@@ -473,19 +471,6 @@
         out.endDocument();
     }
 
-    /** Use this when you actually want to post a notification or toast.
-     *
-     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
-     */
-    private boolean noteNotificationOp(String pkg, int uid) {
-        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
-                != AppOpsManager.MODE_ALLOWED) {
-            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
-            return false;
-        }
-        return true;
-    }
-
     /** Use this to check if a package can post a notification or toast. */
     private boolean checkNotificationOp(String pkg, int uid) {
         return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
@@ -1154,7 +1139,7 @@
         final File systemDir = new File(Environment.getDataDirectory(), "system");
         mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
 
-        syncBlockDb();
+        loadPolicyFile();
 
         // This is a ManagedServices object that keeps track of the listeners.
         mListeners = notificationListeners;
@@ -1267,46 +1252,6 @@
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
     }
 
-    /**
-     * Make sure the XML config and the the AppOps system agree about blocks.
-     */
-    private void syncBlockDb() {
-        loadPolicyFile();
-
-        // sync bans from ranker into app opps
-        Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
-        for(Entry<Integer, String> ban : packageBans.entrySet()) {
-            final int uid = ban.getKey();
-            final String packageName = ban.getValue();
-            setNotificationsEnabledForPackageImpl(packageName, uid, false);
-        }
-
-        // sync bans from app opps into ranker
-        packageBans.clear();
-        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-            final int userId = user.getUserHandle().getIdentifier();
-            final PackageManager packageManager = getContext().getPackageManager();
-            List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
-            final int packageCount = packages.size();
-            for (int p = 0; p < packageCount; p++) {
-                final String packageName = packages.get(p).packageName;
-                try {
-                    final int uid = packageManager.getPackageUidAsUser(packageName, userId);
-                    if (!checkNotificationOp(packageName, uid)) {
-                        packageBans.put(uid, packageName);
-                    }
-                } catch (NameNotFoundException e) {
-                    // forget you
-                }
-            }
-        }
-        for (Entry<Integer, String> ban : packageBans.entrySet()) {
-            mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
-        }
-
-        savePolicyFile();
-    }
-
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -1328,19 +1273,6 @@
         }
     }
 
-    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
-        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
-
-        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
-                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
-
-        // Now, cancel any outstanding notifications that are part of a just-disabled app
-        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
-            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
-                    UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
-        }
-    }
-
     private void updateListenerHintsLocked() {
         final int hints = calculateHints();
         if (hints == mListenerHints) return;
@@ -1522,7 +1454,8 @@
                     isPackageSuspendedForUser(pkg, Binder.getCallingUid());
 
             if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
-                    (!noteNotificationOp(pkg, Binder.getCallingUid()) || isPackageSuspended)) {
+                    (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
+                            || isPackageSuspended)) {
                 Slog.e(TAG, "Suppressing toast from package " + pkg
                         + (isPackageSuspended
                                 ? " due to package suspended by administrator."
@@ -1643,8 +1576,12 @@
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
             checkCallerIsSystem();
 
-            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
             mRankingHelper.setEnabled(pkg, uid, enabled);
+            // Now, cancel any outstanding notifications that are part of a just-disabled app
+            if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
+                        UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
+            }
             savePolicyFile();
         }
 
@@ -1662,8 +1599,8 @@
         @Override
         public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
             checkCallerIsSystemOrSameApp(pkg);
-            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
-                    == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
+
+            return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
         }
 
         @Override
@@ -3400,8 +3337,7 @@
         }
 
         final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE
-                || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE
-                || !noteNotificationOp(pkg, callingUid);
+                || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
         if (isBlocked) {
             Slog.e(TAG, "Suppressing notification from package by user request.");
             usageStats.registerBlocked(r);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 55cb2f3..3dea783 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -21,8 +21,6 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.util.Preconditions;
 
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -35,8 +33,6 @@
 import android.metrics.LogMaker;
 import android.os.Build;
 import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.text.TextUtils;
@@ -189,6 +185,10 @@
                                 safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
                                 safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
                                 safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+                        r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                        r.priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+                        r.visibility = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+                        r.showBadge = safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
 
                         final int innerDepth = parser.getDepth();
                         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 6625331..ac7b763 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -77,8 +77,9 @@
     private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
     private static final boolean DEBUG = false;
 
-    private static final int DEFAULT_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE
-            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+    private static final int DEFAULT_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES;
 
     private static final String AUDIO_MIME_TYPE = "audio/mpeg";
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cdfc691..cb6bb1e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7701,7 +7701,10 @@
             default:
                 return null;
         }
-        if (pattern.length == 1) {
+        if (pattern.length == 0) {
+            // No vibration
+            return null;
+        } else if (pattern.length == 1) {
             // One-shot vibration
             return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
         } else {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cf5bfc9..7ffcf1c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3125,10 +3125,6 @@
         if (reason == null) {
             reason = "";
         }
-        if (reason.equals(PowerManager.REBOOT_RECOVERY)
-                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
-            reason = "recovery";
-        }
 
         // If the reason is "quiescent", it means that the boot process should proceed
         // without turning on the screen/lights.
@@ -3137,6 +3133,15 @@
         if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
             sQuiescent = true;
             reason = "";
+        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
+            sQuiescent = true;
+            reason = reason.substring(0,
+                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
+        }
+
+        if (reason.equals(PowerManager.REBOOT_RECOVERY)
+                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
+            reason = "recovery";
         }
 
         if (sQuiescent) {
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 65e3ec0..f769261 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -78,6 +78,9 @@
     // requires that the duration of the two animations are the same.
     SurfaceControl thumbnail;
     int thumbnailTransactionSeq;
+    // TODO(b/62029108): combine both members into a private one. Create a member function to set
+    // the thumbnail layer to +1 to the highest layer position and replace all setter instances
+    // with this function. Remove all unnecessary calls to both variables in other classes.
     int thumbnailLayer;
     int thumbnailForceAboveLayer;
     Animation thumbnailAnimation;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 172ec48..01a3143 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -257,9 +257,20 @@
         w.mLayer = layer;
         w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
                 + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
-                && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
-            w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+        if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
+            if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
+                w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+            }
+            // TODO(b/62029108): the entire contents of the if statement should call the refactored
+            // function to set the thumbnail layer for w.AppToken
+            int highestLayer = w.mAppToken.getHighestAnimLayer();
+            if (highestLayer > 0) {
+                if (w.mAppToken.mAppAnimator.thumbnail != null
+                        && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
+                    w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
+                    w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
+                }
+            }
         }
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 836f0b8..66849c3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -677,7 +677,6 @@
         VibratorService vibrator = null;
         IStorageManager storageManager = null;
         NetworkManagementService networkManagement = null;
-        IpSecService ipSecService = null;
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
@@ -1035,15 +1034,6 @@
                     reportWtf("starting NetworkManagement Service", e);
                 }
                 traceEnd();
-
-                traceBeginAndSlog("StartIpSecService");
-                try {
-                    ipSecService = IpSecService.create(context);
-                    ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
-                } catch (Throwable e) {
-                    reportWtf("starting IpSec Service", e);
-                }
-                traceEnd();
             }
 
             if (!disableNonCoreServices && !disableTextServices) {
@@ -1669,7 +1659,6 @@
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
         final MediaRouterService mediaRouterF = mediaRouter;
         final MmsServiceBroker mmsServiceF = mmsService;
-        final IpSecService ipSecServiceF = ipSecService;
         final WindowManagerService windowManagerF = wm;
 
         // We now tell the activity manager it is okay to run third party
@@ -1734,13 +1723,6 @@
                         .networkScoreAndNetworkManagementServiceReady();
             }
             traceEnd();
-            traceBeginAndSlog("MakeIpSecServiceReady");
-            try {
-                if (ipSecServiceF != null) ipSecServiceF.systemReady();
-            } catch (Throwable e) {
-                reportWtf("making IpSec Service ready", e);
-            }
-            traceEnd();
             traceBeginAndSlog("MakeNetworkStatsServiceReady");
             try {
                 if (networkStatsF != null) networkStatsF.systemReady();
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 35a72c8..e4c6690 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -191,6 +191,11 @@
     private static final int IPV4_ANY_HOST_ADDRESS = 0;
     private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
 
+    // Traffic class and Flow label are not byte aligned. Luckily we
+    // don't care about either value so we'll consider bytes 1-3 of the
+    // IPv6 header as don't care.
+    private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1;
+    private static final int IPV6_FLOW_LABEL_LEN = 3;
     private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
     private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
     private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
@@ -473,8 +478,13 @@
 
             RaEvent.Builder builder = new RaEvent.Builder();
 
-            // Ignore the checksum.
+            // Ignore the flow label and low 4 bits of traffic class.
             int lastNonLifetimeStart = addNonLifetime(0,
+                    IPV6_FLOW_LABEL_OFFSET,
+                    IPV6_FLOW_LABEL_LEN);
+
+            // Ignore the checksum.
+            lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
                     ICMP6_RA_CHECKSUM_OFFSET,
                     ICMP6_RA_CHECKSUM_LEN);
 
@@ -565,9 +575,14 @@
             for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
                 int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
 
+                // The flow label is in mNonLifetimes, but it's not a lifetime.
+                if (offset == IPV6_FLOW_LABEL_OFFSET) {
+                    continue;
+                }
+
                 // The checksum is in mNonLifetimes, but it's not a lifetime.
                 if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
-                     continue;
+                    continue;
                 }
 
                 final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
@@ -629,6 +644,11 @@
                 if ((i + 1) < mNonLifetimes.size()) {
                     Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
                     int offset = nonLifetime.first + nonLifetime.second;
+
+                    // Skip the Flow label.
+                    if (offset == IPV6_FLOW_LABEL_OFFSET) {
+                        continue;
+                    }
                     // Skip the checksum.
                     if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
                         continue;
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 06b5821..9f7c515 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -27,6 +27,7 @@
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -50,15 +51,11 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.TestableContext;
-import android.testing.TestableSettingsProvider;
 import android.util.ArrayMap;
-import android.util.Slog;
 import android.util.Xml;
 
 import java.io.BufferedInputStream;
@@ -87,10 +84,10 @@
 public class RankingHelperTest extends NotificationTestCase {
     private static final String PKG = "com.android.server.notification";
     private static final int UID = 0;
-    private static final UserHandle USER = UserHandle.getUserHandleForUid(UID);
+    private static final UserHandle USER = UserHandle.of(0);
     private static final String UPDATED_PKG = "updatedPkg";
-    private static final int UID2 = 1111111;
-    private static final UserHandle USER2 = UserHandle.getUserHandleForUid(UID2);
+    private static final int UID2 = 1111;
+    private static final UserHandle USER2 = UserHandle.of(10);
     private static final String TEST_CHANNEL_ID = "test_channel_id";
 
     @Mock NotificationUsageStats mUsageStats;
@@ -199,24 +196,21 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
-        serializer.startTag(null, "ranking");
         mHelper.writeXml(serializer, forBackup);
-        serializer.endTag(null, "ranking");
         serializer.endDocument();
         serializer.flush();
-
         for (String channelId : channelIds) {
             mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
         }
         return baos;
     }
 
-    private void loadStreamXml(ByteArrayOutputStream stream) throws Exception {
+    private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())),
                 null);
         parser.nextTag();
-        mHelper.readXml(parser, false);
+        mHelper.readXml(parser, forRestore);
     }
 
     private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
@@ -323,7 +317,7 @@
                 channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
         mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
 
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         assertTrue(mHelper.canShowBadge(PKG, UID));
         assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
@@ -354,6 +348,70 @@
     }
 
     @Test
+    public void testChannelXmlForBackup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setDescription("descriptions for all");
+        channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel2.enableLights(true);
+        channel2.setBypassDnd(true);
+        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel2.enableVibration(false);
+        channel2.setGroup(ncg.getId());
+        channel2.setLightColor(Color.BLUE);
+
+        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+
+        mHelper.setShowBadge(PKG, UID, true);
+
+        mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+                channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG}, new int[]{UID, UID2});
+
+        mHelper.setShowBadge(UPDATED_PKG, UID2, true);
+
+        loadStreamXml(baos, true);
+
+        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
+        assertTrue(mHelper.canShowBadge(PKG, UID));
+        assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+        compareChannels(channel2,
+                mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+
+        List<NotificationChannelGroup> actualGroups =
+                mHelper.getNotificationChannelGroups(PKG, UID, false).getList();
+        boolean foundNcg = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (ncg.getId().equals(actual.getId())) {
+                foundNcg = true;
+                compareGroups(ncg, actual);
+            } else if (ncg2.getId().equals(actual.getId())) {
+                compareGroups(ncg2, actual);
+            }
+        }
+        assertTrue(foundNcg);
+
+        boolean foundChannel2Group = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
+                foundChannel2Group = true;
+                break;
+            }
+        }
+        assertTrue(foundChannel2Group);
+    }
+
+    @Test
     public void testChannelXml_backup() throws Exception {
         NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
         NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
@@ -397,7 +455,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID);
 
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
                 NotificationChannel.DEFAULT_CHANNEL_ID, false);
@@ -417,7 +475,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID);
 
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
                 PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
@@ -465,7 +523,7 @@
         final ApplicationInfo upgraded = new ApplicationInfo();
         upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
         when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         // Default Channel should be gone.
         assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
@@ -483,7 +541,7 @@
         final ApplicationInfo upgraded = new ApplicationInfo();
         upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
         when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         // Default Channel should be gone.
         assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
@@ -497,7 +555,7 @@
         mHelper.createNotificationChannel(PKG, UID,
                 new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
 
-        loadStreamXml(baos);
+        loadStreamXml(baos, false);
 
         // Should still have the newly created channel that wasn't in the xml.
         assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
@@ -1271,5 +1329,4 @@
         assertFalse(mHelper.badgingEnabled(USER));
         assertTrue(mHelper.badgingEnabled(USER2));
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 32eee84..6618a69 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -115,6 +115,10 @@
                 "mChildAppWindowAbove");
         mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
                 "mChildAppWindowBelow");
+
+        // Adding a display will cause freezing the display. Make sure to wait until it's unfrozen
+        // to not run into race conditions with the tests.
+        waitUntilHandlersIdle();
     }
 
     @After
@@ -135,6 +139,9 @@
             mDisplayContent.removeImmediately();
             sWm.mInputMethodTarget = null;
         }
+
+        // Wait until everything is really cleaned up.
+        waitUntilHandlersIdle();
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 6107895..640c9e1 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -269,6 +269,23 @@
     }
 
     /**
+     * Dumps the events in a timeline format.
+     * @param pw The {@link IndentingPrintWriter} to write to.
+     * @hide
+     */
+    public static void dumpEventsTimeline(IndentingPrintWriter pw) {
+        // If the Events logger has not been initialized, then there have been no events logged.
+        // Don't load it now!
+        synchronized (sSingletonSync) {
+            if (sEventManager != null) {
+                getEventManager().dumpEventsTimeline(pw);
+            } else {
+                pw.println("No Historical Events Logged.");
+            }
+        }
+    }
+
+    /**
      * Enable or disable extended telecom logging.
      *
      * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java
index 2cd1b96..fddbfce 100644
--- a/telecomm/java/android/telecom/Logging/EventManager.java
+++ b/telecomm/java/android/telecom/Logging/EventManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.telecom.Log;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
@@ -27,6 +28,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.IllegalFormatException;
@@ -35,6 +37,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.stream.Collectors;
 
 /**
  * A utility class that provides the ability to define Events that a subsystem deems important, and
@@ -49,6 +52,7 @@
     public static final String TAG = "Logging.Events";
     @VisibleForTesting
     public static final int DEFAULT_EVENTS_TO_CACHE = 10;  // Arbitrarily chosen.
+    private final DateFormat sDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
 
     public interface Loggable {
         /**
@@ -169,7 +173,6 @@
             }
         }
 
-        private final DateFormat sDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
         private final List<Event> mEvents = new LinkedList<>();
         private final Loggable mRecordEntry;
 
@@ -308,6 +311,41 @@
         pw.decreaseIndent();
     }
 
+    /**
+     * Dumps events in a timeline format.
+     * @param pw The {@link IndentingPrintWriter} to output the timeline to.
+     * @hide
+     */
+    public void dumpEventsTimeline(IndentingPrintWriter pw) {
+        pw.println("Historical Events (sorted by time):");
+
+        // Flatten event records out for sorting.
+        List<Pair<Loggable, Event>> events = new ArrayList<>();
+        for (EventRecord er : mEventRecords) {
+            for (Event ev : er.getEvents()) {
+                events.add(new Pair<>(er.getRecordEntry(), ev));
+            }
+        }
+
+        // Sort by event time.
+        Comparator<Pair<Loggable, Event>> byEventTime = (e1, e2) -> {
+          return Long.compare(e1.second.time, e2.second.time);
+        };
+        events.sort(byEventTime);
+
+        pw.increaseIndent();
+        for (Pair<Loggable, Event> event : events) {
+            pw.print(sDateFormat.format(new Date(event.second.time)));
+            pw.print(",");
+            pw.print(event.first.getId());
+            pw.print(",");
+            pw.print(event.second.eventId);
+            pw.print(",");
+            pw.println(event.second.data);
+        }
+        pw.decreaseIndent();
+    }
+
     public void changeEventCacheSize(int newSize) {
 
         // Resize the event queue.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b4f3d69..9caf9d0 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -113,6 +113,15 @@
         </activity>
 
         <activity
+            android:name="DrawIntoHwBitmapActivity"
+            android:label="Bitmaps/DrawIntoHwBitmap">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="PathOffsetActivity"
                 android:label="Path/Offset">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
new file mode 100644
index 0000000..faabdfc
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
@@ -0,0 +1,65 @@
+/*
+ * 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.test.hwui;
+
+import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
+import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
+import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_RARELY;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.GraphicBuffer;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.Surface;
+import android.view.ThreadedRenderer;
+import android.widget.ImageView;
+
+public class DrawIntoHwBitmapActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ImageView view = new ImageView(this);
+        view.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        setContentView(view);
+        view.setImageBitmap(createBitmap());
+    }
+
+    Bitmap createBitmap() {
+        RenderNode node = RenderNode.create("HwuiCanvas", null);
+        node.setLeftTopRightBottom(0, 0, 500, 500);
+        node.setClipToBounds(false);
+        DisplayListCanvas canvas = node.start(500, 500);
+        Paint p = new Paint();
+        p.setColor(Color.BLACK);
+        p.setTextSize(20 * getResources().getDisplayMetrics().density);
+        canvas.drawColor(0xFF2196F3);
+        p.setColor(0xFFBBDEFB);
+        canvas.drawRect(0, 0, 500, 100, p);
+        p.setColor(Color.BLACK);
+        canvas.drawText("Hello, World!", 0, 90, p);
+        node.end(canvas);
+        return ThreadedRenderer.createHardwareBitmap(node, 500, 500);
+    }
+}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index d4896264..6bf3b6b 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -1066,10 +1066,15 @@
         final int ROUTE_LIFETIME  = 400;
         // Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000.
         final int DNSSL_LIFETIME  = 2000;
+        final int VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET = ETH_HEADER_LEN;
+        // IPv6, traffic class = 0, flow label = 0x12345
+        final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345;
 
         // Verify RA is passed the first time
         ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
         basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
+                VERSION_TRAFFIC_CLASS_FLOW_LABEL);
         basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
         basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
         basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME);
@@ -1080,6 +1085,13 @@
         testRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
 
+        ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
+        basePacket.clear();
+        newFlowLabelPacket.put(basePacket);
+        // Check that changes are ignored in every byte of the flow label.
+        newFlowLabelPacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
+                VERSION_TRAFFIC_CLASS_FLOW_LABEL + 0x11111);
+
         // Ensure zero-length options cause the packet to be silently skipped.
         // Do this before we test other packets. http://b/29586253
         ByteBuffer zeroLengthOptionPacket = ByteBuffer.wrap(
@@ -1145,6 +1157,7 @@
         // Verify that current program filters all five RAs:
         program = ipManagerCallback.getApfProgram();
         verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
+        verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
         verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
         verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME);
         verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME);
diff --git a/tests/CoreTests/android/core/NsdServiceInfoTest.java b/tests/net/java/android/net/nsd/NsdServiceInfoTest.java
similarity index 78%
rename from tests/CoreTests/android/core/NsdServiceInfoTest.java
rename to tests/net/java/android/net/nsd/NsdServiceInfoTest.java
index 5bf0167..e48b522 100644
--- a/tests/CoreTests/android/core/NsdServiceInfoTest.java
+++ b/tests/net/java/android/net/nsd/NsdServiceInfoTest.java
@@ -1,11 +1,32 @@
-package android.core;
+/*
+ * Copyright (C) 2014 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.
+ */
 
-import android.test.AndroidTestCase;
+package android.net.nsd;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.StrictMode;
 import android.net.nsd.NsdServiceInfo;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
 import java.util.Arrays;
@@ -14,8 +35,12 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class NsdServiceInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NsdServiceInfoTest {
 
     public final static InetAddress LOCALHOST;
     static {
@@ -30,6 +55,7 @@
         LOCALHOST = _host;
     }
 
+    @Test
     public void testLimits() throws Exception {
         NsdServiceInfo info = new NsdServiceInfo();
 
@@ -85,6 +111,7 @@
         assertTrue(info.getTxtRecord().length == 1300);
     }
 
+    @Test
     public void testParcel() throws Exception {
         NsdServiceInfo emptyInfo = new NsdServiceInfo();
         checkParcelable(emptyInfo);
@@ -139,25 +166,25 @@
         NsdServiceInfo result = reader.getParcelable("test_info");
 
         // Assert equality of base fields.
-        assertEquality(original.getServiceName(), result.getServiceName());
-        assertEquality(original.getServiceType(), result.getServiceType());
-        assertEquality(original.getHost(), result.getHost());
+        assertEquals(original.getServiceName(), result.getServiceName());
+        assertEquals(original.getServiceType(), result.getServiceType());
+        assertEquals(original.getHost(), result.getHost());
         assertTrue(original.getPort() == result.getPort());
 
         // Assert equality of attribute map.
         Map<String, byte[]> originalMap = original.getAttributes();
         Map<String, byte[]> resultMap = result.getAttributes();
-        assertEquality(originalMap.keySet(), resultMap.keySet());
+        assertEquals(originalMap.keySet(), resultMap.keySet());
         for (String key : originalMap.keySet()) {
             assertTrue(Arrays.equals(originalMap.get(key), resultMap.get(key)));
         }
     }
 
-    public void assertEquality(Object expected, Object result) {
-        assertTrue(expected == result || expected.equals(result));
-    }
-
     public void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) {
-        assertTrue(null == shouldBeEmpty.getTxtRecord());
+        byte[] txtRecord = shouldBeEmpty.getTxtRecord();
+        if (txtRecord == null || txtRecord.length == 0) {
+            return;
+        }
+        fail("NsdServiceInfo.getTxtRecord did not return null but " + Arrays.toString(txtRecord));
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 7a3cddf..5deb80a 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -26,6 +26,7 @@
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
+import java.util.Arrays;
 import java.util.Locale;
 
 /**
@@ -189,6 +190,23 @@
         return out.toString();
     }
 
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof WifiSsid)) {
+            return false;
+        }
+        WifiSsid that = (WifiSsid) thatObject;
+        return Arrays.equals(octets.toByteArray(), that.octets.toByteArray());
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(octets.toByteArray());
+    }
+
     private boolean isArrayAllZeroes(byte[] ssidBytes) {
         for (int i = 0; i< ssidBytes.length; i++) {
             if (ssidBytes[i] != 0) return false;
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index d8667e6..115b86d 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -100,7 +100,12 @@
      * @param serviceSpecificInfo The service specific information (arbitrary
      *            byte array) provided by the peer as part of its discovery
      *            configuration.
-     * @param matchFilter The filter which resulted in this service discovery.
+     * @param matchFilter The filter which resulted in this service discovery. For
+     * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED},
+     * {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE} discovery sessions this is the publisher's
+     *                    match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
+     *                    {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
+     *                    is the subscriber's match filter.
      */
     public void onServiceDiscovered(PeerHandle peerHandle,
             byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index c7bdb7b..e5794c5 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -16,25 +16,43 @@
 
 package android.net.wifi;
 
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiSsid}.
  */
 public class WifiSsidTest {
 
-    private static final byte[] TEST_SSID =
-            new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+    private static final String TEST_SSID = "Test SSID";
+    private static final byte[] TEST_SSID_BYTES = TEST_SSID.getBytes(StandardCharsets.US_ASCII);
+
     /**
      * Check that createFromByteArray() works.
      */
     @Test
     public void testCreateFromByteArray() {
-        WifiSsid wifiSsid = WifiSsid.createFromByteArray(TEST_SSID);
+        WifiSsid wifiSsid = WifiSsid.createFromByteArray(TEST_SSID_BYTES);
         assertTrue(wifiSsid != null);
-        assertEquals(new String(TEST_SSID), wifiSsid.toString());
+        assertEquals(TEST_SSID, wifiSsid.toString());
+    }
+
+    /**
+     * Verify that SSID created from byte array and string with the same content are equal.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testEquals() throws Exception {
+        WifiSsid fromBytes = WifiSsid.createFromByteArray(TEST_SSID_BYTES);
+        WifiSsid fromString = WifiSsid.createFromAsciiEncoded(TEST_SSID);
+        assertTrue(fromBytes != null);
+        assertTrue(fromString != null);
+        assertEquals(fromBytes, fromString);
     }
 }