Merge "Dump ZenModeControllerImpl for debugging purposes."
diff --git a/Android.bp b/Android.bp
index dd5ef92..ca456e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@
         "core/java/android/app/timedetector/ITimeDetectorService.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
+        "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
         "core/java/android/app/usage/IStorageStatsManager.aidl",
         "core/java/android/app/usage/IUsageStatsManager.aidl",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 98dc237..b432baa 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -26,6 +26,7 @@
 import android.app.slice.SliceManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timezone.RulesManager;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.IUsageStatsManager;
@@ -1033,6 +1034,13 @@
                             throws ServiceNotFoundException {
                         return new TimeDetector();
                     }});
+        registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
+                new CachedServiceFetcher<TimeZoneDetector>() {
+                    @Override
+                    public TimeZoneDetector createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new TimeZoneDetector();
+                    }});
     }
 
     /**
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
new file mode 100644
index 0000000..ef2cbab
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+/**
+ * System private API to comunicate with time zone detector service.
+ *
+ * <p>Used by parts of the Android system with signals associated with the device's time zone to
+ * provide information to the Time Zone Detector Service.
+ *
+ * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
+ * for more complete documentation.
+ *
+ *
+ * {@hide}
+ */
+interface ITimeZoneDetectorService {
+  void stubbedCall();
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
new file mode 100644
index 0000000..be3c764
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+
+/**
+ * The interface through which system components can send signals to the TimeZoneDetectorService.
+ * @hide
+ */
+@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
+public final class TimeZoneDetector {
+
+    private static final String TAG = "timezonedetector.TimeZoneDetector";
+    private static final boolean DEBUG = false;
+
+    private final ITimeZoneDetectorService mITimeZoneDetectorService;
+
+    public TimeZoneDetector() throws ServiceNotFoundException {
+        mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+
+    }
+    /**
+     * Does nothing.
+     * TODO: Remove this when the service implementation is built out.
+     */
+    public void stubbedCall() {
+        if (DEBUG) {
+            Log.d(TAG, "stubbedCall called");
+        }
+        try {
+            mITimeZoneDetectorService.stubbedCall();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5e96b92..c515bce 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3061,6 +3061,7 @@
             CROSS_PROFILE_APPS_SERVICE,
             //@hide: SYSTEM_UPDATE_SERVICE,
             //@hide: TIME_DETECTOR_SERVICE,
+            //@hide: TIME_ZONE_DETECTOR_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -4244,6 +4245,15 @@
     public static final String TIME_DETECTOR_SERVICE = "time_detector";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6edcec1..d548889 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6985,8 +6985,9 @@
         private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR;
 
         /**
-         * The {@link ComponentName} string of the selected subtype of the selected spell checker
-         * service which is one of the services managed by the text service manager.
+         * {@link android.view.textservice.SpellCheckerSubtype#hashCode()} of the selected subtype
+         * of the selected spell checker service which is one of the services managed by the text
+         * service manager.
          *
          * @hide
          */
@@ -6994,7 +6995,7 @@
                 "selected_spell_checker_subtype";
 
         private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
-                COMPONENT_NAME_VALIDATOR;
+                ANY_INTEGER_VALIDATOR;
 
         /**
          * Whether spell checker is enabled or not.
@@ -9023,6 +9024,14 @@
             "location_background_throttle_package_whitelist";
 
         /**
+         * Maximum staleness allowed for last location when returned to clients with only foreground
+         * location permissions.
+         * @hide
+         */
+        public static final String LOCATION_LAST_LOCATION_MAX_AGE_MILLIS =
+                "location_last_location_max_age_millis";
+
+        /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 01562b3..6f1bd78 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -33,7 +33,6 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -487,8 +486,9 @@
          * intent. The possible values for this extra are
          * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
          *
-         * @deprecated No longer in use. If client ise interested in information about what
-         * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices.
+         * @deprecated No longer in use. If client is interested in information about what
+         * changed, it should use the ACTION_CHECK_TTS_DATA
+         * intent to discover available voices.
          */
         @Deprecated
         public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4eb85ac..d1a0f77 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -41,7 +41,6 @@
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.print.PrintDocumentAdapter;
-import android.security.KeyChain;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -143,7 +142,7 @@
  *       This class is called when something that might impact a
  *       browser UI happens, for instance, progress updates and
  *       JavaScript alerts are sent here (see <a
- * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
+ * href="{@docRoot}guide/webapps/debugging.html">Debugging Web Apps</a>).
  *   </li>
  *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
  *       It will be called when things happen that impact the
@@ -1671,9 +1670,8 @@
     /**
      * Clears the client certificate preferences stored in response
      * to proceeding/cancelling client cert requests. Note that WebView
-     * automatically clears these preferences when it receives a
-     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
-     * shared by all the WebViews that are created by the embedder application.
+     * automatically clears these preferences when the system keychain is updated.
+     * The preferences are shared by all the WebViews that are created by the embedder application.
      *
      * @param onCleared  A runnable to be invoked when client certs are cleared.
      *                   The runnable will be called in UI thread.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index c22dd13..6b3bd65 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -272,6 +272,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
                     Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index f1d43bf..b55d2ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -16,6 +16,10 @@
 
 package com.android.settingslib.drawer;
 
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -169,4 +173,11 @@
             return new Tile[size];
         }
     };
+
+    public boolean isPrimaryProfileOnly() {
+        String profile = metaData != null ?
+            metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+        profile = (profile != null ? profile : PROFILE_ALL);
+        return TextUtils.equals(profile, PROFILE_PRIMARY);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 3549abc..96ed0cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.annotation.VisibleForTesting;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -169,6 +170,34 @@
     public static final String SETTING_PKG = "com.android.settings";
 
     /**
+     * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
+     * the app will always be run in the primary profile.
+     *
+     * @see #META_DATA_KEY_PROFILE
+     */
+    public static final String PROFILE_PRIMARY = "primary_profile_only";
+
+    /**
+     * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
+     * will be presented with a dialog to choose the profile the app will be run in.
+     *
+     * @see #META_DATA_KEY_PROFILE
+     */
+    public static final String PROFILE_ALL = "all_profiles";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the profile in which the app should be run when the device has a managed profile.
+     * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
+     * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
+     * run in the primary profile.
+     *
+     * @see #PROFILE_PRIMARY
+     * @see #PROFILE_ALL
+     */
+    public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
+
+    /**
      * Build a list of DashboardCategory. Each category must be defined in manifest.
      * eg: .Settings$DeviceSettings
      * @deprecated
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
new file mode 100644
index 0000000..996a122
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -0,0 +1,48 @@
+package com.android.settingslib.drawer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
+import android.os.Bundle;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.junit.Test;
+
+@RunWith(RobolectricTestRunner.class)
+public class TileTest {
+
+    private Tile mTile;
+
+    @Before
+    public void setUp() {
+        mTile = new Tile();
+        mTile.metaData = new Bundle();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
+        mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
+        assertThat(mTile.isPrimaryProfileOnly()).isTrue();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
+        mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_nullMetadata_shouldReturnFalse() {
+        mTile.metaData = null;
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+}
diff --git a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 0899d35..0000000
--- a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 2266449..0000000
--- a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 3328add..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index ed651da..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 06e1202..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 975055c..07f1ee0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -137,8 +137,10 @@
 
     <color name="remote_input_accent">#eeeeee</color>
 
-    <color name="quick_step_track_background_dark">#61000000</color>
-    <color name="quick_step_track_background_light">#33FFFFFF</color>
+    <color name="quick_step_track_background_background_dark">#1F000000</color>
+    <color name="quick_step_track_background_background_light">#33FFFFFF</color>
+    <color name="quick_step_track_background_foreground_dark">#38000000</color>
+    <color name="quick_step_track_background_foreground_light">#59FFFFFF</color>
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 6ee3189..3c52e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -126,20 +126,21 @@
     }
 
     public void applyMobileState(MobileIconState state) {
+        boolean requestLayout = false;
         if (state == null) {
+            requestLayout = getVisibility() != View.GONE;
             setVisibility(View.GONE);
             mState = null;
-            return;
-        }
-
-        if (mState == null) {
+        } else if (mState == null) {
+            requestLayout = true;
             mState = state.copy();
             initViewState();
-            return;
+        } else if (!mState.equals(state)) {
+            requestLayout = updateState(state.copy());
         }
 
-        if (!mState.equals(state)) {
-            updateState(state.copy());
+        if (requestLayout) {
+            requestLayout();
         }
     }
 
@@ -162,20 +163,24 @@
         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
         mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
                 ? View.VISIBLE : View.GONE);
     }
 
-    private void updateState(MobileIconState state) {
+    private boolean updateState(MobileIconState state) {
+        boolean needsLayout = false;
+
         setContentDescription(state.contentDescription);
         if (mState.visible != state.visible) {
             mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+            needsLayout = true;
         }
         if (mState.strengthId != state.strengthId) {
             mMobileDrawable.setLevel(state.strengthId);
         }
         if (mState.typeId != state.typeId) {
+            needsLayout |= state.typeId == 0 || mState.typeId == 0;
             if (state.typeId != 0) {
                 mMobileType.setContentDescription(state.typeContentDescription);
                 mMobileType.setImageResource(state.typeId);
@@ -188,11 +193,16 @@
         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
         mInoutContainer.setVisibility((state.activityIn || state.activityOut)
                 ? View.VISIBLE : View.GONE);
 
+        needsLayout |= state.roaming != mState.roaming
+                || state.activityIn != mState.activityIn
+                || state.activityOut != mState.activityOut;
+
         mState = state;
+        return needsLayout;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 513cd32..f3fc99e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -191,23 +191,26 @@
     }
 
     public void applyWifiState(WifiIconState state) {
+        boolean requestLayout = false;
+
         if (state == null) {
+            requestLayout = getVisibility() != View.GONE;
             setVisibility(View.GONE);
             mState = null;
-            return;
-        }
-
-        if (mState == null) {
+        } else if (mState == null) {
+            requestLayout = true;
             mState = state.copy();
             initViewState();
+        } else if (!mState.equals(state)) {
+            requestLayout = updateState(state.copy());
         }
 
-        if (!mState.equals(state)) {
-            updateState(state.copy());
+        if (requestLayout) {
+            requestLayout();
         }
     }
 
-    private void updateState(WifiIconState state) {
+    private boolean updateState(WifiIconState state) {
         setContentDescription(state.contentDescription);
         if (mState.resId != state.resId && state.resId >= 0) {
             NeutralGoodDrawable drawable = NeutralGoodDrawable
@@ -222,11 +225,17 @@
                 (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
         mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
         mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE);
+
+        boolean needsLayout = state.activityIn != mState.activityIn
+                ||state.activityOut != mState.activityOut;
+
         if (mState.visible != state.visible) {
+            needsLayout |= true;
             setVisibility(state.visible ? View.VISIBLE : View.GONE);
         }
 
         mState = state;
+        return needsLayout;
     }
 
     private void initViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index e0e991b..e052e53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -203,7 +203,7 @@
         v.set(icon);
         v.setStaticDrawableColor(mColor);
         v.setDecorColor(mColor);
-        addView(v, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
+        addView(v, 0, createLayoutParams());
     }
 
     public void addDemoWifiView(WifiIconState state) {
@@ -223,7 +223,7 @@
         mWifiView = view;
         mWifiView.applyWifiState(state);
         mWifiView.setStaticDrawableColor(mColor);
-        addView(view, viewIndex);
+        addView(view, viewIndex, createLayoutParams());
     }
 
     public void updateWifiState(WifiIconState state) {
@@ -244,7 +244,7 @@
 
         // mobile always goes at the end
         mMobileViews.add(view);
-        addView(view, getChildCount());
+        addView(view, getChildCount(), createLayoutParams());
     }
 
     public void updateMobileState(MobileIconState state) {
@@ -290,6 +290,10 @@
         return null;
     }
 
+    private LayoutParams createLayoutParams() {
+        return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+    }
+
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 765cc49..9a37cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -27,16 +27,16 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.graphics.Shader;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.FloatProperty;
@@ -55,7 +55,6 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.internal.graphics.ColorUtils;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub.
@@ -66,6 +65,7 @@
     private static final int ANIM_IN_DURATION_MS = 150;
     private static final int ANIM_OUT_DURATION_MS = 134;
     private static final float TRACK_SCALE = 0.95f;
+    private static final float GRADIENT_WIDTH = .75f;
 
     private NavigationBarView mNavigationBarView;
 
@@ -79,9 +79,9 @@
     private boolean mIsRTL;
     private float mTrackAlpha;
     private float mTrackScale = TRACK_SCALE;
-    private int mLightTrackColor;
-    private int mDarkTrackColor;
     private float mDarkIntensity;
+    private RadialGradient mHighlight;
+    private float mHighlightCenter;
     private AnimatorSet mTrackAnimator;
     private ButtonDispatcher mHitTarget;
     private View mCurrentNavigationBarView;
@@ -89,14 +89,13 @@
 
     private final Handler mHandler = new Handler();
     private final Rect mTrackRect = new Rect();
-    private final Drawable mTrackDrawable;
     private final OverviewProxyService mOverviewEventSender;
     private final int mTrackThickness;
     private final int mTrackEndPadding;
     private final Context mContext;
     private final Matrix mTransformGlobalMatrix = new Matrix();
     private final Matrix mTransformLocalMatrix = new Matrix();
-    private final ArgbEvaluator mTrackColorEvaluator = new ArgbEvaluator();
+    private final Paint mTrackPaint = new Paint();
 
     private final FloatProperty<QuickStepController> mTrackAlphaProperty =
             new FloatProperty<QuickStepController>("TrackAlpha") {
@@ -157,7 +156,8 @@
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
         mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
         mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
-        mTrackDrawable = context.getDrawable(R.drawable.qs_scrubber_track).mutate();
+        mTrackPaint.setAntiAlias(true);
+        mTrackPaint.setDither(true);
     }
 
     public void setComponents(NavigationBarView navigationBarView) {
@@ -292,6 +292,8 @@
                     } catch (RemoteException e) {
                         Log.e(TAG, "Failed to send progress of quick scrub.", e);
                     }
+                    mHighlightCenter = x;
+                    mNavigationBarView.invalidate();
                 }
                 break;
             }
@@ -315,18 +317,18 @@
         if (!mNavigationBarView.isQuickScrubEnabled()) {
             return;
         }
-        int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
-                mDarkTrackColor);
-        int colorAlpha = ColorUtils.setAlphaComponent(color,
-                (int) (Color.alpha(color) * mTrackAlpha));
-        mTrackDrawable.setTint(colorAlpha);
+        mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
 
         // Scale the track, but apply the inverse scale from the nav bar
+        final float radius = mTrackRect.height() / 2;
         canvas.save();
+        float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
+        canvas.translate(translate, 0);
         canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
                 1f / mNavigationBarView.getScaleY(),
                 mTrackRect.centerX(), mTrackRect.centerY());
-        mTrackDrawable.draw(canvas);
+        canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
+                mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
         canvas.restore();
     }
 
@@ -351,12 +353,20 @@
             x2 = x1 + width - 2 * mTrackEndPadding;
         }
         mTrackRect.set(x1, y1, x2, y2);
-        mTrackDrawable.setBounds(mTrackRect);
+        updateHighlight();
     }
 
     @Override
     public void onDarkIntensityChange(float intensity) {
+        final float oldIntensity = mDarkIntensity;
         mDarkIntensity = intensity;
+
+        // When in quick scrub, invalidate gradient if changing intensity from black to white and
+        // vice-versa
+        if (mNavigationBarView.isQuickScrubEnabled()
+                && Math.round(intensity) != Math.round(oldIntensity)) {
+            updateHighlight();
+        }
         mNavigationBarView.invalidate();
     }
 
@@ -425,10 +435,8 @@
         }
 
         if (!mQuickScrubActive) {
+            updateHighlight();
             mQuickScrubActive = true;
-            mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
-            mDarkTrackColor = mContext.getColor(R.color.quick_step_track_background_dark);
-
             ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
                     PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
                     PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
@@ -503,6 +511,22 @@
         mQuickScrubActive = false;
         mAllowGestureDetection = false;
         mCurrentNavigationBarView = null;
+        updateHighlight();
+    }
+
+    private void updateHighlight() {
+        int colorBase, colorGrad;
+        if (mDarkIntensity > 0.5f) {
+            colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
+            colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
+        } else {
+            colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
+            colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
+        }
+        mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
+                mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
+                Shader.TileMode.CLAMP);
+        mTrackPaint.setShader(mHighlight);
     }
 
     private boolean proxyMotionEvents(MotionEvent event) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5ab932..de02e81 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -153,6 +153,10 @@
     // default background throttling interval if not overriden in settings
     private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
 
+    // Default value for maximum age of last location returned to applications with foreground-only
+    // location permissions.
+    private static final long DEFAULT_LAST_LOCATION_MAX_AGE_MS = 20 * 60 * 1000;
+
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -241,6 +245,9 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
 
+    // Maximum age of last location returned to clients with foreground-only location permissions.
+    private long mLastLocationMaxAgeMs;
+
     private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
 
     private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -308,7 +315,8 @@
                     }
                 }
             };
-            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
+            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
 
             PackageManager.OnPermissionsChangedListener permissionListener
                     = new PackageManager.OnPermissionsChangedListener() {
@@ -341,6 +349,7 @@
             updateUserProfiles(mCurrentUserId);
 
             updateBackgroundThrottlingWhitelistLocked();
+            updateLastLocationMaxAgeLocked();
 
             // prepare providers
             loadProvidersLocked();
@@ -370,6 +379,18 @@
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+                true,
+                new ContentObserver(mLocationHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            updateLastLocationMaxAgeLocked();
+                        }
+                    }
+                }
+        );
+        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(
                         Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
                 true,
@@ -382,6 +403,7 @@
                         }
                     }
                 }, UserHandle.USER_ALL);
+
         mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
 
         // listen for user change
@@ -841,6 +863,7 @@
             for (String p : mUpdateRecords.keySet()) {
                 s.append(" ").append(mUpdateRecords.get(p).toString());
             }
+            s.append(" monitoring location: ").append(mOpMonitoring);
             s.append("]");
             return s.toString();
         }
@@ -913,7 +936,7 @@
                 }
             } else {
                 if (!allowMonitoring
-                        || mAppOps.checkOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
+                        || mAppOps.noteOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
                         != AppOpsManager.MODE_ALLOWED) {
                     mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
                     return false;
@@ -1541,7 +1564,7 @@
     boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
-            if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+            if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
         }
@@ -1857,6 +1880,14 @@
                 Arrays.asList(setting.split(",")));
     }
 
+    private void updateLastLocationMaxAgeLocked() {
+        mLastLocationMaxAgeMs =
+                Settings.Global.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                        DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+    }
+
     private boolean isThrottlingExemptLocked(Identity identity) {
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
@@ -2262,6 +2293,17 @@
                 if (location == null) {
                     return null;
                 }
+
+                // Don't return stale location to apps with foreground-only location permission.
+                String op = getResolutionPermission(allowedResolutionLevel);
+                long locationAgeMs = SystemClock.elapsedRealtime() -
+                        location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+                if ((locationAgeMs > mLastLocationMaxAgeMs)
+                        && (mAppOps.unsafeCheckOp(op, uid, packageName)
+                            == AppOpsManager.MODE_FOREGROUND)) {
+                    return null;
+                }
+
                 if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
                     Location noGPSLocation = location.getExtraLocation(
                             Location.EXTRA_NO_GPS_LOCATION);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 36b04fc..c6a8712 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -955,12 +955,18 @@
     @GuardedBy("mSeparateChallengeLock")
     private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, boolean enabled,
             String managedUserPassword) {
+        final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
         setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
-        if (enabled) {
-            mStorage.removeChildProfileLock(userId);
-            removeKeystoreProfileKey(userId);
-        } else {
-            tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+        try {
+            if (enabled) {
+                mStorage.removeChildProfileLock(userId);
+                removeKeystoreProfileKey(userId);
+            } else {
+                tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+            }
+        } catch (IllegalStateException e) {
+            setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, old, userId);
+            throw e;
         }
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
new file mode 100644
index 0000000..5f71b0b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.content.Context;
+import android.util.Slog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+    private static final String TAG = "timezonedetector.TimeZoneDetectorService";
+
+    public static class Lifecycle extends SystemService {
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+            // Publish the binder service so it can be accessed from other (appropriately
+            // permissioned) processes.
+            publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
+        }
+    }
+
+    private final Context mContext;
+
+    private static TimeZoneDetectorService create(Context context) {
+        return new TimeZoneDetectorService(context);
+    }
+
+    public TimeZoneDetectorService(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void stubbedCall() {
+        // Empty call for initial tests.
+        Slog.d(TAG, "stubbedCall() called");
+        // TODO: Remove when there are real methods.
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        // TODO: Implement when there is state.
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4456ed0..3321ced 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -233,6 +233,8 @@
             "com.android.internal.car.CarServiceHelperService";
     private static final String TIME_DETECTOR_SERVICE_CLASS =
             "com.android.server.timedetector.TimeDetectorService$Lifecycle";
+    private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
+            "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -1251,6 +1253,14 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("StartTimeZoneDetectorService");
+            try {
+                mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
+            } catch (Throwable e) {
+                reportWtf("starting StartTimeZoneDetectorService service", e);
+            }
+            traceEnd();
+
             if (!isWatch) {
                 traceBeginAndSlog("StartSearchManagerService");
                 try {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..19d31cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for the {@link TimeZoneDetectorService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+    private TimeZoneDetectorService mTimeZoneDetectorService;
+
+    @Before
+    public void setUp() {
+        final Context context = InstrumentationRegistry.getContext();
+        mTimeZoneDetectorService = new TimeZoneDetectorService(context);
+    }
+
+    @Test
+    public void testStubbedCall() {
+        mTimeZoneDetectorService.stubbedCall();
+    }
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index d537699..b77881e 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -341,15 +341,15 @@
         }
     }
 
+    /** @hide */
+    protected Context mContext;
+    /** @hide */
+    protected final Object mLock = new Object();
+
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
     private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    /**
-     * @hide
-     */
-    protected Context mContext;
-    private final Object mLock = new Object();
     private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
             = new RemoteCallbackList<>();
     private Capabilities mCapabilityStatus = new Capabilities();
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index bc790fb..7681aef 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -22,25 +22,25 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telecom.TelecomManager;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsCallSessionImplBase;
-import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsMmTelListener;
 import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.telephony.ims.stub.ImsEcbmImplBase;
 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
 import android.util.Log;
 
-import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.ImsReasonInfo;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsUt;
-import android.telephony.ims.ImsCallSession;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -61,20 +61,16 @@
     private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
 
         @Override
-        public void setListener(IImsMmTelListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setListener(l);
-            }
+        public void setListener(IImsMmTelListener l) {
+            MmTelFeature.this.setListener(l);
         }
 
         @Override
         public int getFeatureState() throws RemoteException {
-            synchronized (mLock) {
-                try {
-                    return MmTelFeature.this.getFeatureState();
-                } catch (Exception e) {
-                    throw new RemoteException(e.getMessage());
-                }
+            try {
+                return MmTelFeature.this.getFeatureState();
+            } catch (Exception e) {
+                throw new RemoteException(e.getMessage());
             }
         }
 
@@ -138,10 +134,8 @@
         }
 
         @Override
-        public int queryCapabilityStatus() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
-            }
+        public int queryCapabilityStatus() {
+            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
         }
 
         @Override
@@ -158,7 +152,7 @@
 
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
-                IImsCapabilityCallback c) throws RemoteException {
+                IImsCapabilityCallback c) {
             synchronized (mLock) {
                 MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
             }
@@ -173,10 +167,8 @@
         }
 
         @Override
-        public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setSmsListener(l);
-            }
+        public void setSmsListener(IImsSmsListener l) {
+            MmTelFeature.this.setSmsListener(l);
         }
 
         @Override
@@ -364,9 +356,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProcessCallResult {}
 
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
     private IImsMmTelListener mListener;
 
     /**
@@ -376,9 +365,9 @@
     private void setListener(IImsMmTelListener listener) {
         synchronized (mLock) {
             mListener = listener;
-        }
-        if (mListener != null) {
-            onFeatureReady();
+            if (mListener != null) {
+                onFeatureReady();
+            }
         }
     }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index ef5912b..8719a23 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -208,6 +208,15 @@
   }
 };
 
+// A chunk of text in the XML string within a CDATA tags.
+class CdataSegmentNode : public SegmentNode {
+ public:
+
+  void Build(StringBuilder* builder) const override {
+    builder->AppendText(data, /* preserve_spaces */ true);
+  }
+};
+
 // A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
 class SpanNode : public Node {
  public:
@@ -244,6 +253,7 @@
   std::vector<Node*> node_stack;
   node_stack.push_back(&root);
 
+  bool cdata_block = false;
   bool saw_span_node = false;
   SegmentNode* first_segment = nullptr;
   SegmentNode* last_segment = nullptr;
@@ -253,11 +263,15 @@
     const xml::XmlPullParser::Event event = parser->event();
 
     // First take care of any SegmentNodes that should be created.
-    if (event == xml::XmlPullParser::Event::kStartElement ||
-        event == xml::XmlPullParser::Event::kEndElement) {
+    if (event == xml::XmlPullParser::Event::kStartElement
+        || event == xml::XmlPullParser::Event::kEndElement
+        || event == xml::XmlPullParser::Event::kCdataStart
+        || event == xml::XmlPullParser::Event::kCdataEnd) {
       if (!current_text.empty()) {
-        std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+        std::unique_ptr<SegmentNode> segment_node = (cdata_block)
+            ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>();
         segment_node->data = std::move(current_text);
+
         last_segment = node_stack.back()->AddChild(std::move(segment_node));
         if (first_segment == nullptr) {
           first_segment = last_segment;
@@ -333,6 +347,16 @@
         }
       } break;
 
+      case xml::XmlPullParser::Event::kCdataStart: {
+        cdata_block = true;
+        break;
+      }
+
+      case xml::XmlPullParser::Event::kCdataEnd: {
+        cdata_block = false;
+        break;
+      }
+
       default:
         // ignore.
         break;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 5711dc3..ee496d5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -989,4 +989,40 @@
   ASSERT_FALSE(TestParse(input));
 }
 
+TEST_F(ResourceParserTest, ParseCData) {
+  std::string input = R"(
+      <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)";
+
+  ASSERT_TRUE(TestParse(input));
+  String* output = test::GetValue<String>(&table_, "string/foo");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe"));
+
+  // Double quotes should not change the state of whitespace processing
+  input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]>      World</string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo2");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>'  World").data()));
+
+  // Cdata blocks should not have their whitespace trimmed
+  input = R"(<string name="foo3">     <![CDATA[ text ]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo3");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string(" text ").data()));
+
+  input = R"(<string name="foo4">     <![CDATA[]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo4");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("").data()));
+
+  input = R"(<string name="foo5">     <![CDATA[    ]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo5");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("    ").data()));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 560077c..c48765b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -797,16 +797,20 @@
     : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
 }
 
-StringBuilder& StringBuilder::AppendText(const std::string& text) {
+StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
   if (!error_.empty()) {
     return *this;
   }
 
+  // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
+  // to preserve spaces
+  preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;
+
   const size_t previous_len = xml_string_.text.size();
   Utf8Iterator iter(text);
   while (iter.HasNext()) {
     char32_t codepoint = iter.Next();
-    if (!quote_ && iswspace(codepoint)) {
+    if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
       if (!last_codepoint_was_space_) {
         // Emit a space if it's the first.
         xml_string_.text += ' ';
@@ -827,7 +831,6 @@
           case U't':
             xml_string_.text += '\t';
             break;
-
           case U'n':
             xml_string_.text += '\n';
             break;
@@ -855,12 +858,12 @@
             break;
         }
       }
-    } else if (!preserve_spaces_ && codepoint == U'"') {
+    } else if (!preserve_spaces && codepoint == U'"') {
       // Only toggle the quote state when we are not preserving spaces.
       quote_ = !quote_;
 
-    } else if (!quote_ && codepoint == U'\'') {
-      // This should be escaped.
+    } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
+      // This should be escaped when we are not preserving spaces
       error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
       return *this;
 
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 7af2fe0..410ef28 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -267,8 +267,10 @@
   // single quotations can be used without escaping them.
   explicit StringBuilder(bool preserve_spaces = false);
 
-  // Appends a chunk of text.
-  StringBuilder& AppendText(const std::string& text);
+  // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and
+  // single quotations can be used without escaping them for this append. Otherwise, the
+  // StringBuilder will behave as it was constructed.
+  StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false);
 
   // Starts a Span (tag) with the given name. The name is expected to be of the form:
   //  "tag_name;attr1=value;attr2=value;"
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 11f3fa3..5ce4640 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -254,6 +254,29 @@
 TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
   EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
               Eq("\""));
+
+  // Single quotes should be able to be used without escaping them when preserving spaces and the
+  // spaces should not be trimmed
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys ")
+                  .AppendText(" 'this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow    ")
+                  .to_string(),
+              Eq(" hey guys  'this is so cool'  wow "));
+
+  // Reading a double quote while preserving spaces should not change the quote state
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys ")
+                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow    ")
+                  .to_string(),
+              Eq(" hey guys  \"this is so cool'  wow "));
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys\"  ")
+                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow  \"  ")
+                  .to_string(),
+              Eq(" hey guys   \"this is so cool'  wow   "));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 402e5a4..a023494 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -38,6 +38,7 @@
                               EndNamespaceHandler);
   XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
   XML_SetCommentHandler(parser_, CommentDataHandler);
+  XML_SetCdataSectionHandler(parser_, StartCdataSectionHandler, EndCdataSectionHandler);
   event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
 }
 
@@ -287,6 +288,22 @@
                                       parser->depth_, comment});
 }
 
+void XMLCALL XmlPullParser::StartCdataSectionHandler(void* user_data) {
+  XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+  parser->event_queue_.push(EventData{Event::kCdataStart,
+                                      XML_GetCurrentLineNumber(parser->parser_),
+                                      parser->depth_ });
+}
+
+void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
+  XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+  parser->event_queue_.push(EventData{Event::kCdataEnd,
+                                      XML_GetCurrentLineNumber(parser->parser_),
+                                      parser->depth_ });
+}
+
 Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
                                  const StringPiece& name) {
   auto iter = parser->FindAttribute("", name);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 63db66f..6ebaa28 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -52,6 +52,8 @@
     kEndElement,
     kText,
     kComment,
+    kCdataStart,
+    kCdataEnd,
   };
 
   /**
@@ -159,6 +161,8 @@
   static void XMLCALL EndElementHandler(void* user_data, const char* name);
   static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
   static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
+  static void XMLCALL StartCdataSectionHandler(void* user_data);
+  static void XMLCALL EndCdataSectionHandler(void* user_data);
 
   struct EventData {
     Event event;
@@ -223,6 +227,10 @@
       return out << "Text";
     case XmlPullParser::Event::kComment:
       return out << "Comment";
+    case XmlPullParser::Event::kCdataStart:
+      return out << "CdataStart";
+    case XmlPullParser::Event::kCdataEnd:
+      return out << "CdataEnd";
   }
   return out;
 }
@@ -240,6 +248,8 @@
       case Event::kText:
       case Event::kComment:
       case Event::kStartElement:
+      case Event::kCdataStart:
+      case Event::kCdataEnd:
         return true;
       default:
         break;