Merge changes from topic "revert_save_info" into pi-dev

* changes:
  Document compatibility mode limitations.
  Revert "Always set FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE on SaveInfo when on compat mode."
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ee667c2..1b6b5a0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -80,8 +80,7 @@
  * {@link #getBondedDevices()}; start device discovery with
  * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
  * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
- * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
  * </p>
  * <p>This class is thread safe.</p>
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 4ed2500..ef1b0bd 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -701,10 +701,14 @@
      * <p>If the local device has already exposed services when this function
      * is called, a service update notification will be sent to all clients.
      *
+     * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
+     * whether this service has been added successfully. Do not add another service
+     * before this callback.
+     *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @param service Service to be added to the list of services provided by this device.
-     * @return true, if the service has been added successfully
+     * @return true, if the request to add service has been initiated
      */
     public boolean addService(BluetoothGattService service) {
         if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1d232bf..0b4b921 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4447,8 +4447,7 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeScreenOnCount =
-                dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
+        final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
         if (dischargeScreenOnCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 71b6084..6b16d42 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20700,7 +20700,7 @@
             if (canTakeFocus()) {
                 // We have a robust focus, so parents should no longer be wanting focus.
                 clearParentsWantFocus();
-            } else if (!getViewRootImpl().isInLayout()) {
+            } else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {
                 // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
                 // layout. In this case, there's no guarantee that parent layouts will be evaluated
                 // and thus the safest action is to clear focus here.
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 96016b4..ad50dc0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -277,12 +277,12 @@
      */
     @Nullable
     public static PendingIntent createPendingIntent(
-            @NonNull final Context context, @NonNull final Intent intent) {
+            @NonNull final Context context, @NonNull final Intent intent, int requestCode) {
         switch (getIntentType(intent, context)) {
             case IntentType.ACTIVITY:
-                return PendingIntent.getActivity(context, 0, intent, 0);
+                return PendingIntent.getActivity(context, requestCode, intent, 0);
             case IntentType.SERVICE:
-                return PendingIntent.getService(context, 0, intent, 0);
+                return PendingIntent.getService(context, requestCode, intent, 0);
             default:
                 return null;
         }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 2213355..910fcaa 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -412,7 +412,7 @@
         boolean isPrimaryAction = true;
         for (LabeledIntent labeledIntent : IntentFactory.create(
                 mContext, referenceTime, highestScoringResult, classifiedText)) {
-            RemoteAction action = labeledIntent.asRemoteAction(mContext);
+            final RemoteAction action = labeledIntent.asRemoteAction(mContext);
             if (isPrimaryAction) {
                 // For O backwards compatibility, the first RemoteAction is also written to the
                 // legacy API fields.
@@ -421,7 +421,7 @@
                 builder.setIntent(labeledIntent.getIntent());
                 builder.setOnClickListener(TextClassification.createIntentOnClickListener(
                         TextClassification.createPendingIntent(mContext,
-                                labeledIntent.getIntent())));
+                                labeledIntent.getIntent(), labeledIntent.getRequestCode())));
                 isPrimaryAction = false;
             }
             builder.addAction(action);
@@ -559,14 +559,30 @@
      * Helper class to store the information from which RemoteActions are built.
      */
     private static final class LabeledIntent {
-        private String mTitle;
-        private String mDescription;
-        private Intent mIntent;
 
-        LabeledIntent(String title, String description, Intent intent) {
+        static final int DEFAULT_REQUEST_CODE = 0;
+
+        private final String mTitle;
+        private final String mDescription;
+        private final Intent mIntent;
+        private final int mRequestCode;
+
+        /**
+         * Initializes a LabeledIntent.
+         *
+         * <p>NOTE: {@code reqestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
+         * if distinguishing info (e.g. the classified text) is represented in intent extras only.
+         * In such circumstances, the request code should represent the distinguishing info
+         * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
+         * unique. To be correct, the PendingIntent should be definitely unique but we try a
+         * best effort approach that avoids spamming the system with PendingIntents.
+         */
+        // TODO: Fix the issue mentioned above so the behaviour is correct.
+        LabeledIntent(String title, String description, Intent intent, int requestCode) {
             mTitle = title;
             mDescription = description;
             mIntent = intent;
+            mRequestCode = requestCode;
         }
 
         String getTitle() {
@@ -581,6 +597,10 @@
             return mIntent;
         }
 
+        int getRequestCode() {
+            return mRequestCode;
+        }
+
         RemoteAction asRemoteAction(Context context) {
             final PackageManager pm = context.getPackageManager();
             final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
@@ -602,8 +622,8 @@
                 icon = Icon.createWithResource("android",
                         com.android.internal.R.drawable.ic_more_items);
             }
-            RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
-                    TextClassification.createPendingIntent(context, mIntent));
+            final RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
+                    TextClassification.createPendingIntent(context, mIntent, mRequestCode));
             action.setShouldShowIcon(shouldShowIcon);
             return action;
         }
@@ -659,13 +679,15 @@
                             context.getString(com.android.internal.R.string.email),
                             context.getString(com.android.internal.R.string.email_desc),
                             new Intent(Intent.ACTION_SENDTO)
-                                    .setData(Uri.parse(String.format("mailto:%s", text)))),
+                                    .setData(Uri.parse(String.format("mailto:%s", text))),
+                            LabeledIntent.DEFAULT_REQUEST_CODE),
                     new LabeledIntent(
                             context.getString(com.android.internal.R.string.add_contact),
                             context.getString(com.android.internal.R.string.add_contact_desc),
                             new Intent(Intent.ACTION_INSERT_OR_EDIT)
                                     .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text)));
+                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+                            text.hashCode()));
         }
 
         @NonNull
@@ -679,20 +701,23 @@
                         context.getString(com.android.internal.R.string.dial),
                         context.getString(com.android.internal.R.string.dial_desc),
                         new Intent(Intent.ACTION_DIAL).setData(
-                                Uri.parse(String.format("tel:%s", text)))));
+                                Uri.parse(String.format("tel:%s", text))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             }
             actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.add_contact),
                     context.getString(com.android.internal.R.string.add_contact_desc),
                     new Intent(Intent.ACTION_INSERT_OR_EDIT)
                             .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                            .putExtra(ContactsContract.Intents.Insert.PHONE, text)));
+                            .putExtra(ContactsContract.Intents.Insert.PHONE, text),
+                    text.hashCode()));
             if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
                 actions.add(new LabeledIntent(
                         context.getString(com.android.internal.R.string.sms),
                         context.getString(com.android.internal.R.string.sms_desc),
                         new Intent(Intent.ACTION_SENDTO)
-                                .setData(Uri.parse(String.format("smsto:%s", text)))));
+                                .setData(Uri.parse(String.format("smsto:%s", text))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             }
             return actions;
         }
@@ -706,7 +731,8 @@
                         context.getString(com.android.internal.R.string.map),
                         context.getString(com.android.internal.R.string.map_desc),
                         new Intent(Intent.ACTION_VIEW)
-                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText)))));
+                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
+                        LabeledIntent.DEFAULT_REQUEST_CODE));
             } catch (UnsupportedEncodingException e) {
                 Log.e(LOG_TAG, "Could not encode address", e);
             }
@@ -728,7 +754,8 @@
                     context.getString(com.android.internal.R.string.browse),
                     context.getString(com.android.internal.R.string.browse_desc),
                     new Intent(Intent.ACTION_VIEW, Uri.parse(text))
-                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())));
+                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
+                    LabeledIntent.DEFAULT_REQUEST_CODE));
         }
 
         @NonNull
@@ -754,7 +781,8 @@
                     context.getString(com.android.internal.R.string.view_flight),
                     context.getString(com.android.internal.R.string.view_flight_desc),
                     new Intent(Intent.ACTION_WEB_SEARCH)
-                            .putExtra(SearchManager.QUERY, text)));
+                            .putExtra(SearchManager.QUERY, text),
+                    text.hashCode()));
         }
 
         @NonNull
@@ -765,7 +793,8 @@
             return new LabeledIntent(
                     context.getString(com.android.internal.R.string.view_calendar),
                     context.getString(com.android.internal.R.string.view_calendar_desc),
-                    new Intent(Intent.ACTION_VIEW).setData(builder.build()));
+                    new Intent(Intent.ACTION_VIEW).setData(builder.build()),
+                    LabeledIntent.DEFAULT_REQUEST_CODE);
         }
 
         @NonNull
@@ -781,7 +810,8 @@
                             .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
                                     parsedTime.toEpochMilli())
                             .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
-                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
+                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
+                    parsedTime.hashCode());
         }
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dac100a..f6ac1cc 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4063,7 +4063,8 @@
                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                 mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
                         TextClassification.createPendingIntent(mTextView.getContext(),
-                                textClassification.getIntent())));
+                                textClassification.getIntent(),
+                                createAssistMenuItemPendingIntentRequestCode())));
             }
             final int count = textClassification.getActions().size();
             for (int i = 1; i < count; i++) {
@@ -4121,7 +4122,9 @@
                 final Intent intent = assistMenuItem.getIntent();
                 if (intent != null) {
                     onClickListener = TextClassification.createIntentOnClickListener(
-                            TextClassification.createPendingIntent(mTextView.getContext(), intent));
+                            TextClassification.createPendingIntent(
+                                    mTextView.getContext(), intent,
+                                    createAssistMenuItemPendingIntentRequestCode()));
                 }
             }
             if (onClickListener != null) {
@@ -4132,6 +4135,14 @@
             return true;
         }
 
+        private int createAssistMenuItemPendingIntentRequestCode() {
+            return mTextView.hasSelection()
+                    ? mTextView.getText().subSequence(
+                            mTextView.getSelectionStart(), mTextView.getSelectionEnd())
+                            .hashCode()
+                    : 0;
+        }
+
         private boolean shouldEnableAssistMenuItems() {
             return mTextView.isDeviceProvisioned()
                 && TextClassificationManager.getSettings(mTextView.getContext())
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3118009..f1cc569 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -386,6 +386,38 @@
   EXPECT_EQ(basic::R::array::integerArray1, last_ref);
 }
 
+TEST_F(AssetManager2Test, ResolveDeepIdReference) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  // Set up the resource ids
+  const uint32_t high_ref = assetmanager
+      .GetResourceId("@id/high_ref", "values", "com.android.basic");
+  ASSERT_NE(high_ref, 0u);
+  const uint32_t middle_ref = assetmanager
+      .GetResourceId("@id/middle_ref", "values", "com.android.basic");
+  ASSERT_NE(middle_ref, 0u);
+  const uint32_t low_ref = assetmanager
+      .GetResourceId("@id/low_ref", "values", "com.android.basic");
+  ASSERT_NE(low_ref, 0u);
+
+  // Retrieve the most shallow resource
+  Res_value value;
+  ResTable_config config;
+  uint32_t flags;
+  ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
+                                                    0 /*density_override*/,
+                                                    &value, &config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(middle_ref, value.data);
+
+  // Check that resolving the reference resolves to the deepest id
+  uint32_t last_ref = high_ref;
+  assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
+  EXPECT_EQ(last_ref, low_ref);
+}
+
 TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_.get()});
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 1733b6a..b721ebf 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index b343562..d4b2683 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -78,4 +78,8 @@
         <item type="string" name="test2" />
         <item type="array" name="integerArray1" />
     </overlayable>
+
+    <item name="high_ref" type="id">@id/middle_ref</item>
+    <item name="middle_ref" type="id">@id/low_ref</item>
+    <item name="low_ref" type="id"/>
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index a5f0f24..1ca3c1d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -65,9 +65,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -219,7 +217,7 @@
         stopScan();
         mDevicesFound.clear();
         mSelectedDevice = null;
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
     }
 
     @Override
@@ -265,7 +263,12 @@
             onReadyToShowUI();
         }
         mDevicesFound.add(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
+    }
+
+    private void notifyDataSetChanged() {
+        Handler.getMain().sendMessage(obtainMessage(
+                DevicesAdapter::notifyDataSetChanged, mDevicesAdapter));
     }
 
     //TODO also, on timeout -> call onFailure
@@ -283,7 +286,7 @@
 
     private void onDeviceLost(@Nullable DeviceFilterPair device) {
         mDevicesFound.remove(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        notifyDataSetChanged();
         if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 547cd9a..fe0b35b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -103,10 +103,9 @@
     public void handleBroadcast(Intent intent) {
         String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-            state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                    WifiManager.WIFI_STATE_UNKNOWN);
-            enabled = state == WifiManager.WIFI_STATE_ENABLED;
+            updateWifiState();
         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            updateWifiState();
             final NetworkInfo networkInfo =
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
             connected = networkInfo != null && networkInfo.isConnected();
@@ -128,6 +127,11 @@
         }
     }
 
+    private void updateWifiState() {
+        state = mWifiManager.getWifiState();
+        enabled = state == WifiManager.WIFI_STATE_ENABLED;
+    }
+
     private void updateRssi(int newRssi) {
         rssi = newRssi;
         level = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6c7eda7..5ae43c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -65,10 +65,9 @@
     void setKeyguardShowing(boolean keyguardShowing);
 
     /**
-     * Returns the {@link View} that should expand the quick settings when clicked.
+     * Sets the {@link android.view.View.OnClickListener to be used on elements that expend QS.
      */
-    @Nullable
-    View getExpandView();
+    void setExpandClickListener(View.OnClickListener onClickListener);
 
     default void disable(int state1, int state2, boolean animate) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index abe819b..fd9ddb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +26,7 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
+import android.os.Bundle;
 import android.os.UserManager;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -34,6 +34,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -96,6 +97,7 @@
     private ImageView mMobileRoaming;
     private final int mColorForeground;
     private final CellSignalState mInfo = new CellSignalState();
+    private OnClickListener mExpandClickListener;
 
     public QSFooterImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -140,6 +142,7 @@
         mActivityStarter = Dependency.get(ActivityStarter.class);
         addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
                 oldBottom) -> updateAnimator(right - left));
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
     private void updateAnimator(int width) {
@@ -205,6 +208,11 @@
     }
 
     @Override
+    public void setExpandClickListener(OnClickListener onClickListener) {
+        mExpandClickListener = onClickListener;
+    }
+
+    @Override
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -238,8 +246,20 @@
     }
 
     @Override
-    public View getExpandView() {
-        return findViewById(R.id.expand_indicator);
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
+            if (mExpandClickListener != null) {
+                mExpandClickListener.onClick(null);
+                return true;
+            }
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index b82e355..cbd1ca1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -240,11 +240,6 @@
     @Override
     public void setHeaderClickable(boolean clickable) {
         if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
-
-        View expandView = mFooter.getExpandView();
-        if (expandView != null) {
-            expandView.setClickable(clickable);
-        }
     }
 
     @Override
@@ -369,11 +364,7 @@
 
     @Override
     public void setExpandClickListener(OnClickListener onClickListener) {
-        View expandView = mFooter.getExpandView();
-
-        if (expandView != null) {
-            expandView.setOnClickListener(onClickListener);
-        }
+        mFooter.setExpandClickListener(onClickListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 24b5a34..2ea21c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -114,11 +114,9 @@
         }
     }
 
-    @Nullable
     @Override
-    public View getExpandView() {
+    public void setExpandClickListener(OnClickListener onClickListener) {
         // No view that should expand/collapse the quick settings.
-        return null;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 04bfcdd..66176b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -544,7 +544,12 @@
 
             // Set visibility, may fail if a11y service is active.
             // If invisible, call will stop animation.
-            mNavigationBarView.setRotateButtonVisibility(true);
+            int appliedVisibility = mNavigationBarView.setRotateButtonVisibility(true);
+            if (appliedVisibility == View.VISIBLE) {
+                // If the button will actually become visible and the navbar is about to hide,
+                // tell the statusbar to keep it around for longer
+                mStatusBar.touchAutoHide();
+            }
 
         } else { // Hide
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index c2053b6..6dbe9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -766,13 +766,13 @@
         if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
     }
 
-    public void setRotateButtonVisibility(final boolean visible) {
+    public int setRotateButtonVisibility(final boolean visible) {
         // Never show if a11y is visible
         final boolean adjVisible = visible && !mShowAccessibilityButton;
         final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
 
         // No need to do anything if the request matches the current state
-        if (vis == getRotateSuggestionButton().getVisibility()) return;
+        if (vis == getRotateSuggestionButton().getVisibility()) return vis;
 
         getRotateSuggestionButton().setVisibility(vis);
         mShowRotateButton = visible;
@@ -789,6 +789,9 @@
 
         // Hide/restore other button visibility, if necessary
         updateNavButtonIcons();
+
+        // Return applied visibility
+        return vis;
     }
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 351633b..ca459a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1965,14 +1965,12 @@
 
     @Override
     public void onClick(View v) {
-        if (v.getId() == R.id.expand_indicator) {
-            onQsExpansionStarted();
-            if (mQsExpanded) {
-                flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
-            } else if (mQsExpansionEnabled) {
-                mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
-                flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
-            }
+        onQsExpansionStarted();
+        if (mQsExpanded) {
+            flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
+        } else if (mQsExpansionEnabled) {
+            mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+            flingSettings(0 /* vel */, true /* expand */, null, true /* isClick */);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index d30e777..6591715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -136,10 +136,9 @@
     }
 
     protected void setWifiEnabled(boolean enabled) {
-        Intent i = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        i.putExtra(WifiManager.EXTRA_WIFI_STATE,
+        when(mMockWm.getWifiState()).thenReturn(
                 enabled ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
-        mNetworkController.onReceive(mContext, i);
+        mNetworkController.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
     }
 
     protected void setWifiState(boolean connected, String ssid) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 0c444cb..febce31 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -1284,12 +1284,16 @@
                         if (r.showBadge != DEFAULT_SHOW_BADGE) {
                             record.put("showBadge", Boolean.valueOf(r.showBadge));
                         }
+                        JSONArray channels = new JSONArray();
                         for (NotificationChannel channel : r.channels.values()) {
-                            record.put("channel", channel.toJson());
+                            channels.put(channel.toJson());
                         }
+                        record.put("channels", channels);
+                        JSONArray groups = new JSONArray();
                         for (NotificationChannelGroup group : r.groups.values()) {
-                            record.put("group", group.toJson());
+                            groups.put(group.toJson());
                         }
+                        record.put("groups", groups);
                     } catch (JSONException e) {
                         // pass
                     }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1b6f882..19c6c31 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -586,7 +586,29 @@
 
     out_resource->name.type = ResourceType::kId;
     out_resource->name.entry = maybe_name.value().to_string();
-    out_resource->value = util::make_unique<Id>();
+
+    // Ids either represent a unique resource id or reference another resource id
+    auto item = ParseItem(parser, out_resource, resource_format);
+    if (!item) {
+      return false;
+    }
+
+    String* empty = ValueCast<String>(out_resource->value.get());
+    if (empty && *empty->value == "") {
+      // If no inner element exists, represent a unique identifier
+      out_resource->value = util::make_unique<Id>();
+    } else {
+      // If an inner element exists, the inner element must be a reference to
+      // another resource id
+      Reference* ref = ValueCast<Reference>(out_resource->value.get());
+      if (!ref || ref->name.value().type != ResourceType::kId) {
+        diag_->Error(DiagMessage(out_resource->source)
+                         << "<" << parser->element_name()
+                         << "> inner element must either be a resource reference or empty");
+        return false;
+      }
+    }
+
     return true;
   }
 
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index fc1aeaa..c12b9fa 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -933,4 +933,32 @@
   EXPECT_FALSE(TestParse(input));
 }
 
+TEST_F(ResourceParserTest, ParseIdItem) {
+  std::string input = R"(
+    <item name="foo" type="id">@id/bar</item>
+    <item name="bar" type="id"/>
+    <item name="baz" type="id"></item>)";
+  ASSERT_TRUE(TestParse(input));
+
+  ASSERT_THAT(test::GetValue<Reference>(&table_, "id/foo"), NotNull());
+  ASSERT_THAT(test::GetValue<Id>(&table_, "id/bar"), NotNull());
+  ASSERT_THAT(test::GetValue<Id>(&table_, "id/baz"), NotNull());
+
+  // Reject attribute references
+  input = R"(<item name="foo2" type="id">?attr/bar"</item>)";
+  ASSERT_FALSE(TestParse(input));
+
+  // Reject non-references
+  input = R"(<item name="foo3" type="id">0x7f010001</item>)";
+  ASSERT_FALSE(TestParse(input));
+  input = R"(<item name="foo4" type="id">@drawable/my_image</item>)";
+  ASSERT_FALSE(TestParse(input));
+  input = R"(<item name="foo5" type="id"><string name="biz"></string></item>)";
+  ASSERT_FALSE(TestParse(input));
+
+  // Ids that reference other resource ids cannot be public
+  input = R"(<public name="foo6" type="id">@id/bar6</item>)";
+  ASSERT_FALSE(TestParse(input));
+}
+
 }  // namespace aapt