Merge "Darker extracted gradients." into oc-dr1-dev
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index f3f0ae5..1eac395 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -136,6 +136,11 @@
      * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via
      * the PendingIntent. Use this method of scanning if your process is not always running and it
      * should be started when scan results are available.
+     * <p>
+     * When the PendingIntent is delivered, the Intent passed to the receiver or activity
+     * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE},
+     * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of
+     * the scan.
      *
      * @param filters Optional list of ScanFilters for finding exact BLE devices.
      * @param settings Optional settings for the scan.
diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml
new file mode 100644
index 0000000..f5b8496
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc370/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Template for showing mobile network operator name while WFC is active -->
+    <string-array name="wfcSpnFormats">
+        <item>%s</item>
+        <item>%s Wi-Fi</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml
new file mode 100644
index 0000000..f5b8496
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc720/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Template for showing mobile network operator name while WFC is active -->
+    <string-array name="wfcSpnFormats">
+        <item>%s</item>
+        <item>%s Wi-Fi</item>
+    </string-array>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
new file mode 100644
index 0000000..9554e81
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreference.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 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.settingslib;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v14.preference.PreferenceDialogFragment;
+import android.support.v7.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class CustomDialogPreference extends DialogPreference {
+
+    private CustomPreferenceDialogFragment mFragment;
+
+    public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public CustomDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomDialogPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomDialogPreference(Context context) {
+        super(context);
+    }
+
+    public boolean isDialogOpen() {
+        return getDialog() != null && getDialog().isShowing();
+    }
+
+    public Dialog getDialog() {
+        return mFragment != null ? mFragment.getDialog() : null;
+    }
+
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+    }
+
+    protected void onDialogClosed(boolean positiveResult) {
+    }
+
+    protected void onClick(DialogInterface dialog, int which) {
+    }
+
+    protected void onBindDialogView(View view) {
+    }
+
+    private void setFragment(CustomPreferenceDialogFragment fragment) {
+        mFragment = fragment;
+    }
+
+    public static class CustomPreferenceDialogFragment extends PreferenceDialogFragment {
+
+        public static CustomPreferenceDialogFragment newInstance(String key) {
+            final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+            final Bundle b = new Bundle(1);
+            b.putString(ARG_KEY, key);
+            fragment.setArguments(b);
+            return fragment;
+        }
+
+        private CustomDialogPreference getCustomizablePreference() {
+            return (CustomDialogPreference) getPreference();
+        }
+
+        @Override
+        protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+            super.onPrepareDialogBuilder(builder);
+            getCustomizablePreference().setFragment(this);
+            getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+        }
+
+        @Override
+        public void onDialogClosed(boolean positiveResult) {
+            getCustomizablePreference().onDialogClosed(positiveResult);
+        }
+
+        @Override
+        protected void onBindDialogView(View view) {
+            super.onBindDialogView(view);
+            getCustomizablePreference().onBindDialogView(view);
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            super.onClick(dialog, which);
+            getCustomizablePreference().onClick(dialog, which);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
new file mode 100644
index 0000000..692d211
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.settingslib;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v14.preference.EditTextPreferenceDialogFragment;
+import android.support.v7.preference.EditTextPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+
+public class CustomEditTextPreference extends EditTextPreference {
+
+    private CustomPreferenceDialogFragment mFragment;
+
+    public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomEditTextPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomEditTextPreference(Context context) {
+        super(context);
+    }
+
+    public EditText getEditText() {
+        return mFragment != null ? (EditText) mFragment.getDialog().findViewById(android.R.id.edit)
+                : null;
+    }
+
+    public boolean isDialogOpen() {
+        return getDialog() != null && getDialog().isShowing();
+    }
+
+    public Dialog getDialog() {
+        return mFragment != null ? mFragment.getDialog() : null;
+    }
+
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+    }
+
+    protected void onDialogClosed(boolean positiveResult) {
+    }
+
+    protected void onClick(DialogInterface dialog, int which) {
+    }
+
+    protected void onBindDialogView(View view) {
+    }
+
+    private void setFragment(CustomPreferenceDialogFragment fragment) {
+        mFragment = fragment;
+    }
+
+    public static class CustomPreferenceDialogFragment extends EditTextPreferenceDialogFragment {
+
+        public static CustomPreferenceDialogFragment newInstance(String key) {
+            final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+            final Bundle b = new Bundle(1);
+            b.putString(ARG_KEY, key);
+            fragment.setArguments(b);
+            return fragment;
+        }
+
+        private CustomEditTextPreference getCustomizablePreference() {
+            return (CustomEditTextPreference) getPreference();
+        }
+
+        @Override
+        protected void onBindDialogView(View view) {
+            super.onBindDialogView(view);
+            getCustomizablePreference().onBindDialogView(view);
+        }
+
+        @Override
+        protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+            super.onPrepareDialogBuilder(builder);
+            getCustomizablePreference().setFragment(this);
+            getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+        }
+
+        @Override
+        public void onDialogClosed(boolean positiveResult) {
+            super.onDialogClosed(positiveResult);
+            getCustomizablePreference().onDialogClosed(positiveResult);
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            super.onClick(dialog, which);
+            getCustomizablePreference().onClick(dialog, which);
+        }
+    }
+}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 05678e2..7f7b968 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -827,4 +827,7 @@
     <!-- How far to inset the rounded edges -->
     <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
 
+    <!-- Width of the hollow triangle for empty signal state -->
+    <dimen name="mobile_signal_empty_strokewidth">2dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
index 8809510..4ff10e9 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
@@ -34,12 +34,14 @@
     private final FragmentHostManager mFragmentHostManager;
     private final String mTag;
     private final Extension<T> mExtension;
+    private final int mId;
     private String mOldClass;
 
     private ExtensionFragmentListener(View view, String tag, int id, Extension<T> extension) {
         mTag = tag;
         mFragmentHostManager = FragmentHostManager.get(view);
         mExtension = extension;
+        mId = id;
         mFragmentHostManager.getFragmentManager().beginTransaction()
                 .replace(id, (Fragment) mExtension.get(), mTag)
                 .commit();
@@ -49,7 +51,7 @@
     public void accept(T extension) {
         try {
             Fragment.class.cast(extension);
-            mFragmentHostManager.getExtensionManager().setCurrentExtension(mTag,
+            mFragmentHostManager.getExtensionManager().setCurrentExtension(mId, mTag,
                     mOldClass, extension.getClass().getName(), mExtension.getContext());
             mOldClass = extension.getClass().getName();
         } catch (ClassCastException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 871f113..f8f364d 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -266,16 +266,14 @@
     class ExtensionFragmentManager {
         private final ArrayMap<String, Context> mExtensionLookup = new ArrayMap<>();
 
-        public void setCurrentExtension(@NonNull  String tag, @Nullable String oldClass,
+        public void setCurrentExtension(int id, @NonNull  String tag, @Nullable String oldClass,
                 @NonNull String currentClass, @Nullable Context context) {
-            Fragment fragment = getFragmentManager().findFragmentByTag(tag);
             if (oldClass != null) {
                 mExtensionLookup.remove(oldClass);
             }
             mExtensionLookup.put(currentClass, context);
             getFragmentManager().beginTransaction()
-                    .replace(((View) fragment.getView().getParent()).getId(),
-                            instantiate(context, currentClass, null), tag)
+                    .replace(id, instantiate(context, currentClass, null), tag)
                     .commit();
             reloadFragments();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 32b5862..af2b767 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -38,6 +38,7 @@
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
 
 /**
  * Bouncer between work activities and the activity used to confirm credentials before unlocking
@@ -83,6 +84,7 @@
         // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
         // redaction switched on.
         final View blankView = new View(this);
+        blankView.setContentDescription(getString(R.string.accessibility_desc_work_lock));
         blankView.setBackgroundColor(getPrimaryColor());
         setContentView(blankView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 0373d77..ebf4b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -93,6 +93,7 @@
             ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
                     mActivityManager);
             mMenuController.hideMenu();
+            mTouchHandler.onActivityUnpinned(topPipActivity);
             mNotificationController.onActivityUnpinned(topPipActivity);
 
             SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index ddaeb04..3682ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -25,6 +25,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.IActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -116,7 +117,7 @@
             };
 
     // Behaviour states
-    private int mMenuState;
+    private int mMenuState = MENU_STATE_NONE;
     private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
@@ -212,16 +213,17 @@
     }
 
     public void onActivityPinned() {
-        // Reset some states once we are pinned
-        mMenuState = MENU_STATE_NONE;
-
-        if (mIsMinimized) {
-            setMinimizedStateInternal(false);
-        }
-        cleanUpDismissTarget();
+        cleanUp();
         mShowPipMenuOnAnimationEnd = true;
     }
 
+    public void onActivityUnpinned(ComponentName topPipActivity) {
+        if (topPipActivity == null) {
+            // Clean up state after the last PiP activity is removed
+            cleanUp();
+        }
+    }
+
     public void onPinnedStackAnimationEnded() {
         // Always synchronize the motion helper bounds once PiP animations finish
         mMotionHelper.synchronizePinnedStackBounds();
@@ -729,6 +731,16 @@
         mDismissViewController.destroyDismissTarget();
     }
 
+    /**
+     * Resets some states related to the touch handling.
+     */
+    private void cleanUp() {
+        if (mIsMinimized) {
+            setMinimizedStateInternal(false);
+        }
+        cleanUpDismissTarget();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e6c3520..81ec6a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -141,7 +141,11 @@
                 && mDataController.isMobileDataEnabled();
 
         state.icon = new SignalIcon(cb.mobileSignalIconId);
-        state.state = Tile.STATE_ACTIVE;
+        if (cb.airplaneModeEnabled) {
+            state.state = Tile.STATE_INACTIVE;
+        } else {
+            state.state = Tile.STATE_ACTIVE;
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
index 083da51..3946250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
@@ -21,13 +21,14 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.graphics.Path;
 import android.graphics.Path.Direction;
 import android.graphics.Path.FillType;
 import android.graphics.Path.Op;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.util.LayoutDirection;
@@ -63,6 +64,7 @@
     private static final int STATE_EMPTY = 1;
     private static final int STATE_CUT = 2;
     private static final int STATE_CARRIER_CHANGE = 3;
+    private static final int STATE_AIRPLANE = 4;
 
     private static final long DOT_DELAY = 1000;
 
@@ -97,6 +99,18 @@
     // How far the circle defining the corners is inset from the edges
     private final float mAppliedCornerInset;
 
+    // The easiest way to understand this is as if we set Style.STROKE and draw the triangle,
+    // but that is only theoretically right. Instead, draw the triangle and clip out a smaller
+    // one inset by this amount.
+    private final float mEmptyStrokeWidth;
+    private static final float INV_TAN = 1f / (float) Math.tan(Math.PI / 8f);
+    private final float mEmptyDiagInset;  // == mEmptyStrokeWidth * INV_TAN
+    //TODO: This is needed because drawing the triangle is paramterized on the rounded edges.
+    // We get rounded corners by placing circles at arbitrary points along the legs of the triangle,
+    // but that means we lose the notion of a triangle being strictly half of its containing square.
+    // As a result, here's a value to tweak the insets of the cutout for the no-signal icon.
+    private final float mCutExtraOffset;
+
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final int mDarkModeBackgroundColor;
@@ -106,6 +120,10 @@
     private final Path mFullPath = new Path();
     private final Path mForegroundPath = new Path();
     private final Path mXPath = new Path();
+    // Cut out when STATE_EMPTY
+    private final Path mCutPath = new Path();
+    // Draws the slash when in airplane mode
+    private final SlashArtist mSlash = new SlashArtist();
     private final Handler mHandler;
     private float mOldDarkIntensity = -1;
     private float mNumLevels = 1;
@@ -126,11 +144,18 @@
         mLightModeFillColor =
                 Utils.getDefaultColor(context, R.color.light_mode_icon_color_dual_tone_fill);
         mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size);
+
+        // mCutPath parameters
+        mEmptyStrokeWidth = context.getResources()
+                .getDimensionPixelSize(R.dimen.mobile_signal_empty_strokewidth);
+        mEmptyDiagInset = mEmptyStrokeWidth * INV_TAN;
+
         mHandler = new Handler();
         setDarkIntensity(0);
 
         mAppliedCornerInset = context.getResources()
                 .getDimensionPixelSize(R.dimen.stat_sys_mobile_signal_circle_inset);
+        mCutExtraOffset = mAppliedCornerInset / 2f;
     }
 
     public void setIntrinsicSize(int size) {
@@ -224,9 +249,10 @@
         }
         mFullPath.reset();
         mFullPath.setFillType(FillType.WINDING);
+
         final float width = getBounds().width();
         final float height = getBounds().height();
-        final float padding = (int) (PAD * width);  // Stay on pixel boundary
+        final float padding = Math.round(PAD * width);
         final float cornerRadius = RADIUS_RATIO * height;
         // Offset from circle where the hypotenuse meets the circle
         final float diagOffset = DIAG_OFFSET_MULTIPLIER * cornerRadius;
@@ -292,10 +318,29 @@
             mFullPath.rLineTo(0, cut);
         }
 
-        mPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL);
-        mForegroundPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL);
+        if (mState == STATE_EMPTY) {
+            // Cut out a smaller triangle from the center of mFullPath
+            mCutPath.reset();
+            mCutPath.setFillType(FillType.WINDING);
+            mCutPath.moveTo(width - padding - mEmptyStrokeWidth,
+                    height - padding - mEmptyStrokeWidth);
+            mCutPath.lineTo(width - padding - mEmptyStrokeWidth,
+                    padding + mEmptyDiagInset - mCutExtraOffset);
+            mCutPath.lineTo(padding + mEmptyDiagInset - mCutExtraOffset,
+                    height - padding - mEmptyStrokeWidth);
+            mCutPath.lineTo(width - padding - mEmptyStrokeWidth,
+                    height - padding - mEmptyStrokeWidth);
 
-        if (mState != STATE_CARRIER_CHANGE) {
+            // In empty state, draw the full path as the foreground paint
+            mForegroundPath.set(mFullPath);
+            mFullPath.reset();
+            mForegroundPath.op(mCutPath, Path.Op.DIFFERENCE);
+        } else if (mState == STATE_AIRPLANE) {
+            // Airplane mode is slashed, full-signal
+            mForegroundPath.set(mFullPath);
+            mFullPath.reset();
+            mSlash.draw((int) height, (int) width, canvas, mForegroundPaint);
+        } else if (mState != STATE_CARRIER_CHANGE) {
             mForegroundPath.reset();
             int sigWidth = Math.round(calcFit(mLevel / (mNumLevels - 1)) * (width - 2 * padding));
             mForegroundPath.addRect(padding, padding, padding + sigWidth, height - padding,
@@ -405,4 +450,65 @@
     public static int getEmptyState(int numLevels) {
         return (STATE_EMPTY << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT);
     }
+
+    public static int getAirplaneModeState(int numLevels) {
+        return (STATE_AIRPLANE << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT);
+    }
+
+    private final class SlashArtist {
+        // These values are derived in un-rotated (vertical) orientation
+        private static final float SLASH_WIDTH = 1.8384776f;
+        private static final float SLASH_HEIGHT = 22f;
+        private static final float CENTER_X = 10.65f;
+        private static final float CENTER_Y = 15.869239f;
+        private static final float SCALE = 24f;
+
+        // Bottom is derived during animation
+        private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE;
+        private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE;
+        private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE;
+        private static final float BOTTOM = (CENTER_Y + (SLASH_HEIGHT / 2)) / SCALE;
+        // Draw the slash washington-monument style; rotate to no-u-turn style
+        private static final float ROTATION = -45f;
+
+        private final Path mPath = new Path();
+        private final RectF mSlashRect = new RectF();
+
+        void draw(int height, int width, @NonNull Canvas canvas, Paint paint) {
+            Matrix m = new Matrix();
+            updateRect(
+                    scale(LEFT, width),
+                    scale(TOP, height),
+                    scale(RIGHT, width),
+                    scale(BOTTOM, height));
+
+            mPath.reset();
+            // Draw the slash vertically
+            mPath.addRect(mSlashRect, Direction.CW);
+            m.setRotate(ROTATION, width / 2, height / 2);
+            mPath.transform(m);
+            canvas.drawPath(mPath, paint);
+
+            // Rotate back to vertical, and draw the cut-out rect next to this one
+            m.setRotate(-ROTATION, width / 2, height / 2);
+            mPath.transform(m);
+            m.setTranslate(mSlashRect.width(), 0);
+            mPath.transform(m);
+            mPath.addRect(mSlashRect, Direction.CW);
+            m.setRotate(ROTATION, width / 2, height / 2);
+            mPath.transform(m);
+            canvas.clipOutPath(mPath);
+        }
+
+        void updateRect(float left, float top, float right, float bottom) {
+            mSlashRect.left = left;
+            mSlashRect.top = top;
+            mSlashRect.right = right;
+            mSlashRect.bottom = bottom;
+        }
+
+        private float scale(float frac, int width) {
+            return frac * width;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 67b5596..efce871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -254,6 +254,10 @@
 
     @Override
     public int getQsCurrentIconId() {
+        if (mCurrentState.airplaneMode) {
+            return SignalDrawable.getAirplaneModeState(getNumLevels());
+        }
+
         return getCurrentIconId();
     }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 63e5cac..f696f94 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4119,6 +4119,11 @@
     // OS: O DR
     BLUETOOTH_PAIRING = 1018;
 
+    // ACTION: Collect PSD Signals
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    ACTION_PSD_LOADER = 1019;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e85f96b..cb91f93 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -270,6 +270,12 @@
     }
 
     @Override
+    public void onSwitchUser(int userHandle) {
+        if (sDebug) Slog.d(TAG, "Hiding UI when user switched");
+        mUi.hideAll(null);
+    }
+
+    @Override
     public void onCleanupUser(int userId) {
         synchronized (mLock) {
             removeCachedServiceLocked(userId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1a02e8d..751c054 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -360,8 +360,7 @@
     }
 
     void disableOwnedAutofillServicesLocked(int uid) {
-        if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid
-                != UserHandle.getAppId(uid)) {
+        if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) {
             return;
         }
         final long identity = Binder.clearCallingIdentity();
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index d1fbbf9..c9e2a92 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -191,6 +191,7 @@
                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
         window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
         window.setCloseOnTouchOutside(true);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3daaf8f..36e92d5 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -831,30 +831,41 @@
                     case WifiManager.WIFI_AP_STATE_DISABLING:
                     case WifiManager.WIFI_AP_STATE_FAILED:
                     default:
-                        disableWifiIpServingLocked(curState);
+                        disableWifiIpServingLocked(ifname, curState);
                         break;
                 }
             }
         }
     }
 
-    // TODO: Pass in the interface name and, if non-empty, only turn down IP
-    // serving on that one interface.
-    private void disableWifiIpServingLocked(int apState) {
-        if (DBG) Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" + apState);
+    private void disableWifiIpServingLocked(String ifname, int apState) {
+        mLog.log("Canceling WiFi tethering request - AP_STATE=" + apState);
 
-        // Tell appropriate interface state machines that they should tear
-        // themselves down.
+        // Regardless of whether we requested this transition, the AP has gone
+        // down.  Don't try to tether again unless we're requested to do so.
+        // TODO: Remove this altogether, once Wi-Fi reliably gives us an
+        // interface name with every broadcast.
+        mWifiTetherRequested = false;
+
+        if (!TextUtils.isEmpty(ifname)) {
+            final TetherState ts = mTetherStates.get(ifname);
+            if (ts != null) {
+                ts.stateMachine.unwanted();
+                return;
+            }
+        }
+
         for (int i = 0; i < mTetherStates.size(); i++) {
             TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
             if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
-                tism.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
-                break;  // There should be at most one of these.
+                tism.unwanted();
+                return;
             }
         }
-        // Regardless of whether we requested this transition, the AP has gone
-        // down.  Don't try to tether again unless we're requested to do so.
-        mWifiTetherRequested = false;
+
+        mLog.log("Error disabling Wi-Fi IP serving; " +
+                (TextUtils.isEmpty(ifname) ? "no interface name specified"
+                                           : "specified interface: " + ifname));
     }
 
     private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 881c0ca..86b2551 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -163,6 +163,8 @@
 
     public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
 
+    public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
+
     /**
      * Internals.
      */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5c6521b..89dbc2a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -23,6 +23,7 @@
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
+import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -2823,7 +2824,7 @@
 
             if (theme != context.getThemeResId() || labelRes != 0) {
                 try {
-                    context = context.createPackageContext(packageName, 0);
+                    context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
                     context.setTheme(theme);
                 } catch (PackageManager.NameNotFoundException e) {
                     // Ignore