Merge "Removing some unsed compat APIs"
diff --git a/Android.bp b/Android.bp
index 1586440..7f4756f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -495,6 +495,8 @@
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
         "telephony/java/android/telephony/data/IDataService.aidl",
         "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
+        "telephony/java/android/telephony/data/IQualifiedNetworksService.aidl",
+        "telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsConfig.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index d9befc7..6aef50e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4725,6 +4725,7 @@
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
+    method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -4733,6 +4734,7 @@
     ctor public NotificationStats();
     ctor protected NotificationStats(android.os.Parcel);
     method public int describeContents();
+    method public int getDismissalSentiment();
     method public int getDismissalSurface();
     method public boolean hasDirectReplied();
     method public boolean hasExpanded();
@@ -4741,6 +4743,7 @@
     method public boolean hasSnoozed();
     method public boolean hasViewedSettings();
     method public void setDirectReplied();
+    method public void setDismissalSentiment(int);
     method public void setDismissalSurface(int);
     method public void setExpanded();
     method public void setSeen();
@@ -4749,10 +4752,14 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
     field public static final int DISMISSAL_AOD = 2; // 0x2
-    field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+    field public static final int DISMISSAL_NOT_DISMISSED = -1000; // 0xfffffc18
     field public static final int DISMISSAL_OTHER = 0; // 0x0
     field public static final int DISMISSAL_PEEK = 1; // 0x1
     field public static final int DISMISSAL_SHADE = 3; // 0x3
+    field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
+    field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
+    field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
+    field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
   }
 
   public final class SnoozeCriterion implements android.os.Parcelable {
@@ -5546,6 +5553,19 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public abstract class QualifiedNetworksService extends android.app.Service {
+    ctor public QualifiedNetworksService();
+    method public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityUpdater createNetworkAvailabilityUpdater(int);
+    field public static final java.lang.String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService";
+  }
+
+  public abstract class QualifiedNetworksService.NetworkAvailabilityUpdater implements java.lang.AutoCloseable {
+    ctor public QualifiedNetworksService.NetworkAvailabilityUpdater(int);
+    method public abstract void close();
+    method public final int getSlotIndex();
+    method public final void updateQualifiedNetworkTypes(int, int[]);
+  }
+
 }
 
 package android.telephony.euicc {
@@ -6206,6 +6226,10 @@
     field public static final int STATE_UNAVAILABLE = 0; // 0x0
   }
 
+  public static class ImsFeature.Capabilities {
+    field protected int mCapabilities;
+  }
+
   protected static class ImsFeature.CapabilityCallbackProxy {
     method public void onChangeCapabilityConfigurationError(int, int, int);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 1657de5..33bcce4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5,11 +5,11 @@
     field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
+    field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
-    field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
-    field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
   }
 
 }
@@ -28,11 +28,11 @@
   public class ActivityManager {
     method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
     method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
+    method public void forceStopPackage(java.lang.String);
     method public int getPackageImportance(java.lang.String);
     method public long getTotalRam();
     method public int getUidImportance(int);
     method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
-    method public void forceStopPackage(java.lang.String);
     method public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
   }
 
@@ -53,21 +53,21 @@
   }
 
   public class ActivityTaskManager {
+    method public java.lang.String listAllStacks();
+    method public void moveTaskToStack(int, int, boolean);
+    method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
     method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
     method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+    method public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
     method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
-    method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
-    method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
-    method public static boolean supportsMultiWindow(android.content.Context);
-    method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
-    method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
-    method public void startSystemLockTaskMode(int);
-    method public void stopSystemLockTaskMode();
-    method public void moveTaskToStack(int, int, boolean);
     method public void resizeStack(int, android.graphics.Rect, boolean);
     method public void resizeTask(int, android.graphics.Rect);
-    method public void resizeDockedStack(android.graphics.Rect,android.graphics.Rect);
-    method public java.lang.String listAllStacks();
+    method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
+    method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
+    method public void startSystemLockTaskMode(int);
+    method public void stopSystemLockTaskMode();
+    method public static boolean supportsMultiWindow(android.content.Context);
+    method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
     field public static final int INVALID_STACK_ID = -1; // 0xffffffff
     field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
     field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
@@ -1025,10 +1025,12 @@
 
   public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     ctor public InternalSanitizer();
+    method public abstract android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
 
   public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     ctor public InternalTransformation();
+    method public static boolean batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer, android.service.autofill.InternalTransformation>>);
   }
 
   public abstract class InternalValidator implements android.os.Parcelable android.service.autofill.Validator {
@@ -1092,6 +1094,7 @@
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
+    method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -1104,6 +1107,7 @@
     ctor public NotificationStats();
     ctor protected NotificationStats(android.os.Parcel);
     method public int describeContents();
+    method public int getDismissalSentiment();
     method public int getDismissalSurface();
     method public boolean hasDirectReplied();
     method public boolean hasExpanded();
@@ -1112,6 +1116,7 @@
     method public boolean hasSnoozed();
     method public boolean hasViewedSettings();
     method public void setDirectReplied();
+    method public void setDismissalSentiment(int);
     method public void setDismissalSurface(int);
     method public void setExpanded();
     method public void setSeen();
@@ -1120,10 +1125,14 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
     field public static final int DISMISSAL_AOD = 2; // 0x2
-    field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+    field public static final int DISMISSAL_NOT_DISMISSED = -1000; // 0xfffffc18
     field public static final int DISMISSAL_OTHER = 0; // 0x0
     field public static final int DISMISSAL_PEEK = 1; // 0x1
     field public static final int DISMISSAL_SHADE = 3; // 0x3
+    field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0
+    field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1
+    field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2
+    field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18
   }
 
   public final class SnoozeCriterion implements android.os.Parcelable {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 466d02b..0497618 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -106,7 +106,6 @@
                 new AudioModeChangedHandler());
 
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
-        mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
 
     public void setReceiverHandler(android.os.Handler handler) {
@@ -150,8 +149,7 @@
         for (BluetoothDevice device : bondedDevices) {
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
-                cachedDevice = mDeviceManager.addDevice(device);
-                dispatchDeviceAdded(cachedDevice);
+                mDeviceManager.addDevice(device);
                 deviceAdded = true;
             }
         }
@@ -276,7 +274,6 @@
         public void onReceive(Context context, Intent intent,
                 BluetoothDevice device) {
             short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
-            BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
             String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
             // TODO Pick up UUID. They should be available for 2.1 devices.
             // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
@@ -287,8 +284,6 @@
                         + cachedDevice);
             }
             cachedDevice.setRssi(rssi);
-            cachedDevice.setBtClass(btClass);
-            cachedDevice.setNewName(name);
             cachedDevice.setJustDiscovered(true);
         }
     }
@@ -339,18 +334,9 @@
                     BluetoothDevice.ERROR);
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
-                Log.w(TAG, "CachedBluetoothDevice for device " + device +
-                        " not found, calling readPairedDevices().");
-                if (readPairedDevices()) {
-                    cachedDevice = mDeviceManager.findDevice(device);
-                }
-
-                if (cachedDevice == null) {
-                    Log.w(TAG, "Got bonding state changed for " + device +
-                            ", but we have no record of that device.");
-                    cachedDevice = mDeviceManager.addDevice(device);
-                    dispatchDeviceAdded(cachedDevice);
-                }
+                Log.w(TAG, "Got bonding state changed for " + device +
+                        ", but we have no record of that device.");
+                cachedDevice = mDeviceManager.addDevice(device);
             }
 
             synchronized (mCallbacks) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index c511589..33ee569 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -51,13 +51,9 @@
     private final BluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
     private final BluetoothDevice mDevice;
-    //TODO: consider remove, BluetoothDevice.getName() is already cached
-    private String mName;
     private long mHiSyncId;
     // Need this since there is no method for getting RSSI
     private short mRssi;
-    //TODO: consider remove, BluetoothDevice.getBluetoothClass() is already cached
-    private BluetoothClass mBtClass;
     private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
 
     private final List<LocalBluetoothProfile> mProfiles =
@@ -299,7 +295,7 @@
             }
             return;
         }
-        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
+        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + getName());
     }
 
     private boolean ensurePaired() {
@@ -376,8 +372,6 @@
 
     // TODO: do any of these need to run async on a background thread?
     private void fillData() {
-        fetchName();
-        fetchBtClass();
         updateProfiles();
         fetchActiveDevices();
         migratePhonebookPermissionChoice();
@@ -400,21 +394,15 @@
         return mDevice.getAddress();
     }
 
-    public String getName() {
-        return mName;
-    }
-
     /**
-     * Populate name from BluetoothDevice.ACTION_FOUND intent
+     * Get name from remote device
+     * @return {@link BluetoothDevice#getAliasName()} if
+     * {@link BluetoothDevice#getAliasName()} is not null otherwise return
+     * {@link BluetoothDevice#getAddress()}
      */
-    void setNewName(String name) {
-        if (mName == null) {
-            mName = name;
-            if (mName == null || TextUtils.isEmpty(mName)) {
-                mName = mDevice.getAddress();
-            }
-            dispatchAttributesChanged();
-        }
+    public String getName() {
+        final String aliasName = mDevice.getAliasName();
+        return TextUtils.isEmpty(aliasName) ? getAddress() : aliasName;
     }
 
     /**
@@ -422,9 +410,8 @@
      * @param name new alias name to be set, should never be null
      */
     public void setName(String name) {
-        // Prevent mName to be set to null if setName(null) is called
-        if (name != null && !TextUtils.equals(name, mName)) {
-            mName = name;
+        // Prevent getName() to be set to null if setName(null) is called
+        if (name != null && !TextUtils.equals(name, getName())) {
             mDevice.setAlias(name);
             dispatchAttributesChanged();
         }
@@ -461,19 +448,10 @@
     }
 
     void refreshName() {
-        fetchName();
-        dispatchAttributesChanged();
-    }
-
-    private void fetchName() {
-        mName = mDevice.getAliasName();
-
-        if (TextUtils.isEmpty(mName)) {
-            mName = mDevice.getAddress();
-            if (BluetoothUtils.D) {
-                Log.d(TAG, "Device has no name (yet), use address: " + mName);
-            }
+        if (BluetoothUtils.D) {
+            Log.d(TAG, "Device name: " + getName());
         }
+        dispatchAttributesChanged();
     }
 
     /**
@@ -606,13 +584,6 @@
         return getBondState() == BluetoothDevice.BOND_BONDING;
     }
 
-    /**
-     * Fetches a new value for the cached BT class.
-     */
-    private void fetchBtClass() {
-        mBtClass = mDevice.getBluetoothClass();
-    }
-
     private boolean updateProfiles() {
         ParcelUuid[] uuids = mDevice.getUuids();
         if (uuids == null) return false;
@@ -657,15 +628,6 @@
     }
 
     /**
-     * Refreshes the UI for the BT class, including fetching the latest value
-     * for the class.
-     */
-    void refreshBtClass() {
-        fetchBtClass();
-        dispatchAttributesChanged();
-    }
-
-    /**
      * Refreshes the UI when framework alerts us of a UUID change.
      */
     void onUuidChanged() {
@@ -715,15 +677,8 @@
         }
     }
 
-    void setBtClass(BluetoothClass btClass) {
-        if (btClass != null && mBtClass != btClass) {
-            mBtClass = btClass;
-            dispatchAttributesChanged();
-        }
-    }
-
     public BluetoothClass getBtClass() {
-        return mBtClass;
+        return mDevice.getBluetoothClass();
     }
 
     public List<LocalBluetoothProfile> getProfiles() {
@@ -757,7 +712,7 @@
         }
     }
 
-    private void dispatchAttributesChanged() {
+    void dispatchAttributesChanged() {
         synchronized (mCallbacks) {
             for (Callback callback : mCallbacks) {
                 callback.onDeviceAttributesChanged();
@@ -805,7 +760,7 @@
         if (comparison != 0) return comparison;
 
         // Fallback on name
-        return mName.compareTo(another.mName);
+        return getName().compareTo(another.getName());
     }
 
     public interface Callback {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index f8543fc..47bd853 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -260,7 +260,7 @@
     public synchronized void onBtClassChanged(BluetoothDevice device) {
         CachedBluetoothDevice cachedDevice = findDevice(device);
         if (cachedDevice != null) {
-            cachedDevice.refreshBtClass();
+            cachedDevice.dispatchAttributesChanged();
         }
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5e417c3..c18db11 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -606,4 +606,35 @@
 
         assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
     }
+
+    @Test
+    public void getName_aliasNameNotNull_returnAliasName() {
+        when(mDevice.getAliasName()).thenReturn(DEVICE_NAME);
+
+        assertThat(mCachedDevice.getName()).isEqualTo(DEVICE_NAME);
+    }
+
+    @Test
+    public void getName_aliasNameIsNull_returnAddress() {
+        when(mDevice.getAliasName()).thenReturn(null);
+
+        assertThat(mCachedDevice.getName()).isEqualTo(DEVICE_ADDRESS);
+    }
+
+    @Test
+    public void setName_setDeviceNameIsNotNull() {
+        final String name = "test name";
+        when(mDevice.getAliasName()).thenReturn(DEVICE_NAME);
+
+        mCachedDevice.setName(name);
+
+        verify(mDevice).setAlias(name);
+    }
+
+    @Test
+    public void setName_setDeviceNameIsNull() {
+        mCachedDevice.setName(null);
+
+        verify(mDevice, never()).setAlias(any());
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index f223176..e05f9fd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -241,6 +241,7 @@
         mShadowBluetoothAdapter.setSupportedProfiles(null);
         mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
                 mDeviceManager, mEventManager);
+        mEventManager.registerProfileIntentReceiver();
         // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
         // LocalBluetoothProfileManager created.
         mEventManager.setReceiverHandler(null);
diff --git a/packages/SystemUI/res/drawable-ldrtl/ic_sysbar_back_carmode.xml b/packages/SystemUI/res/drawable-ldrtl/ic_sysbar_back_carmode.xml
deleted file mode 100644
index f91190b..0000000
--- a/packages/SystemUI/res/drawable-ldrtl/ic_sysbar_back_carmode.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="44dp"
-        android:height="44dp"
-        android:viewportWidth="44.0"
-        android:viewportHeight="44.0">
-    <path
-        android:pathData="M35,21.94C35,22.78 34.49,23.58 33.64,24.05L12.44,36.13C11.43,36.7 10.58,36.51 10.11,36.25C9.08,35.67 9,34.56 9,34.09L9,9.91C9,9.35 9.08,8.31 10.09,7.75C10.54,7.49 11.34,7.31 12.33,7.86L33.74,19.95C34.51,20.39 35,21.13 35,21.94L35,21.94ZM12.5,32L30.5,21.96L12.5,12L12.5,32Z"
-        android:strokeColor="#00000000"
-        android:fillType="evenOdd"
-        android:fillColor="#F8F9FA"
-        android:strokeWidth="1"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_back_ime_carmode.xml b/packages/SystemUI/res/drawable/ic_sysbar_back_ime_carmode.xml
deleted file mode 100644
index 542ba9b..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_back_ime_carmode.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="44dp"
-        android:height="44dp"
-        android:viewportWidth="44.0"
-        android:viewportHeight="44.0">
-    <path
-        android:pathData="M22.06,35C21.22,35 20.42,34.49 19.95,33.64L7.87,12.44C7.3,11.43 7.49,10.58 7.75,10.11C8.33,9.08 9.44,9 9.91,9L34.09,9C34.65,9 35.69,9.08 36.25,10.09C36.51,10.54 36.69,11.34 36.14,12.33L24.05,33.74C23.61,34.51 22.87,35 22.06,35L22.06,35ZM12,12.5L22.04,30.5L32,12.5L12,12.5Z"
-        android:strokeColor="#00000000"
-        android:fillType="evenOdd"
-        android:fillColor="#F8F9FA"
-        android:strokeWidth="1"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml
new file mode 100644
index 0000000..57cf83f
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_header.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/title"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:textAppearance="@android:style/TextAppearance.Material.Body2"
+    android:minHeight="28dp"
+    android:textColor="?android:attr/colorAccent"
+    android:text="@string/drag_to_rearrange_tiles" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 4ce6ef6..bb67c54 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -43,7 +43,6 @@
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1"
-            android:paddingTop="28dp"
             android:paddingLeft="@dimen/qs_tile_layout_margin_side"
             android:paddingRight="@dimen/qs_tile_layout_margin_side"
             android:paddingBottom="28dp"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c113745d..2deec5e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1869,6 +1869,9 @@
     <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
     <string name="drag_to_add_tiles">Hold and drag to add tiles</string>
 
+    <!-- Label for header of customize QS [CHAR LIMIT=60] -->
+    <string name="drag_to_rearrange_tiles">Hold and drag to rearrange tiles</string>
+
     <!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] -->
     <string name="drag_to_remove_tiles">Drag here to remove</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 10b92f7..92f5cae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -62,6 +62,7 @@
     private static final int TYPE_TILE = 0;
     private static final int TYPE_EDIT = 1;
     private static final int TYPE_ACCESSIBLE_DROP = 2;
+    private static final int TYPE_HEADER = 3;
     private static final int TYPE_DIVIDER = 4;
 
     private static final long EDIT_ID = 10000;
@@ -112,7 +113,7 @@
 
     public void saveSpecs(QSTileHost host) {
         List<String> newSpecs = new ArrayList<>();
-        for (int i = 0; i < mTiles.size() && mTiles.get(i) != null; i++) {
+        for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) {
             newSpecs.add(mTiles.get(i).spec);
         }
         host.changeTiles(mCurrentSpecs, newSpecs);
@@ -145,6 +146,7 @@
         }
         mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
         mTiles.clear();
+        mTiles.add(null);
         for (int i = 0; i < mCurrentSpecs.size(); i++) {
             final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
             if (tile != null) {
@@ -177,6 +179,9 @@
 
     @Override
     public int getItemViewType(int position) {
+        if (position == 0) {
+            return TYPE_HEADER;
+        }
         if (mAccessibilityAction == ACTION_ADD && position == mEditIndex - 1) {
             return TYPE_ACCESSIBLE_DROP;
         }
@@ -193,6 +198,9 @@
     public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
         final Context context = parent.getContext();
         LayoutInflater inflater = LayoutInflater.from(context);
+        if (viewType == TYPE_HEADER) {
+            return new Holder(inflater.inflate(R.layout.qs_customize_header, parent, false));
+        }
         if (viewType == TYPE_DIVIDER) {
             return new Holder(inflater.inflate(R.layout.qs_customize_tile_divider, parent, false));
         }
@@ -218,6 +226,9 @@
 
     @Override
     public void onBindViewHolder(final Holder holder, int position) {
+        if (holder.getItemViewType() == TYPE_HEADER) {
+            return;
+        }
         if (holder.getItemViewType() == TYPE_DIVIDER) {
             holder.itemView.setVisibility(mTileDividerIndex < mTiles.size() - 1 ? View.VISIBLE
                     : View.INVISIBLE);
@@ -243,7 +254,7 @@
             holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
             holder.mTileView.setContentDescription(mContext.getString(
                     R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel,
-                    position + 1));
+                    position));
             holder.mTileView.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -274,13 +285,13 @@
                     R.string.accessibility_qs_edit_add_tile_label, info.state.label);
         } else if (mAccessibilityAction == ACTION_ADD) {
             info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position + 1);
+                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position);
         } else if (mAccessibilityAction == ACTION_MOVE) {
             info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position + 1);
+                    R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position);
         } else {
             info.state.contentDescription = mContext.getString(
-                    R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
+                    R.string.accessibility_qs_edit_tile_label, position, info.state.label);
         }
         holder.mTileView.handleStateChanged(info.state);
         holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
@@ -403,11 +414,12 @@
     }
 
     private void updateDividerLocations() {
-        // The first null is the edit tiles label, the second null is the tile divider.
-        // If there is no second null, then there are no non-system tiles.
+        // The first null is the header label (index 0) so we can skip it,
+        // the second null is the edit tiles label, the third null is the tile divider.
+        // If there is no third null, then there are no non-system tiles.
         mEditIndex = -1;
         mTileDividerIndex = mTiles.size();
-        for (int i = 0; i < mTiles.size(); i++) {
+        for (int i = 1; i < mTiles.size(); i++) {
             if (mTiles.get(i) == null) {
                 if (mEditIndex == -1) {
                     mEditIndex = i;
@@ -486,7 +498,7 @@
         @Override
         public int getSpanSize(int position) {
             final int type = getItemViewType(position);
-            return type == TYPE_EDIT || type == TYPE_DIVIDER ? 3 : 1;
+            return type == TYPE_EDIT || type == TYPE_DIVIDER || type == TYPE_HEADER ? 3 : 1;
         }
     };
 
@@ -511,7 +523,8 @@
             for (int i = 0; i < childCount; i++) {
                 final View child = parent.getChildAt(i);
                 final ViewHolder holder = parent.getChildViewHolder(child);
-                if (holder.getAdapterPosition() < mEditIndex && !(child instanceof TextView)) {
+                if (holder.getAdapterPosition() == 0 ||
+                        holder.getAdapterPosition() < mEditIndex && !(child instanceof TextView)) {
                     continue;
                 }
 
@@ -569,6 +582,9 @@
         @Override
         public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
                 ViewHolder target) {
+            if (target.getAdapterPosition() == 0){
+                return false;
+            }
             if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) {
                 return target.getAdapterPosition() < mEditIndex;
             }
@@ -577,12 +593,17 @@
 
         @Override
         public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
-            if (viewHolder.getItemViewType() == TYPE_EDIT || viewHolder.getItemViewType() == TYPE_DIVIDER) {
-                return makeMovementFlags(0, 0);
+            switch (viewHolder.getItemViewType()) {
+                case TYPE_EDIT:
+                case TYPE_DIVIDER:
+                case TYPE_HEADER:
+                    // Fall through
+                    return makeMovementFlags(0, 0);
+                default:
+                    int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN
+                            | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT;
+                    return makeMovementFlags(dragFlags, 0);
             }
-            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
-                    | ItemTouchHelper.LEFT;
-            return makeMovementFlags(dragFlags, 0);
         }
 
         @Override
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 3e41cd2..0d9c623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -123,9 +123,9 @@
     private Rect mTmpRect = new Rect();
 
     private KeyButtonDrawable mBackIcon;
-    private KeyButtonDrawable mBackCarModeIcon, mBackLandCarModeIcon;
-    private KeyButtonDrawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
-    private KeyButtonDrawable mHomeDefaultIcon, mHomeCarModeIcon;
+    private KeyButtonDrawable mHomeDefaultIcon;
+    private KeyButtonDrawable mBackCarModeIcon;
+    private KeyButtonDrawable mHomeCarModeIcon;
     private KeyButtonDrawable mRecentIcon;
     private KeyButtonDrawable mDockedIcon;
     private KeyButtonDrawable mImeIcon;
@@ -461,16 +461,9 @@
                 && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
     }
 
-    // TODO(b/80003212): change car mode icons to vector icons.
     private void updateCarModeIcons(Context ctx) {
-        mBackCarModeIcon = getDrawable(ctx,
-                R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
-        mBackLandCarModeIcon = mBackCarModeIcon;
-        mBackAltCarModeIcon = getDrawable(ctx,
-                R.drawable.ic_sysbar_back_ime_carmode, R.drawable.ic_sysbar_back_ime_carmode);
-        mBackAltLandCarModeIcon = mBackAltCarModeIcon;
-        mHomeCarModeIcon = getDrawable(ctx,
-                R.drawable.ic_sysbar_home_carmode, R.drawable.ic_sysbar_home_carmode);
+        mBackCarModeIcon = getDrawable(ctx, R.drawable.ic_sysbar_back_carmode);
+        mHomeCarModeIcon = getDrawable(ctx, R.drawable.ic_sysbar_home_carmode);
     }
 
     private void reloadNavIcons() {
@@ -575,11 +568,9 @@
                 darkContext.getDrawable(icon), hasShadow);
     }
 
-    private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int lightIcon,
-            @DrawableRes int darkIcon) {
-        // Legacy image icons using separate light and dark images will not support shadows
-        return KeyButtonDrawable.create(ctx, ctx.getDrawable(lightIcon),
-            ctx.getDrawable(darkIcon), false /* hasShadow */);
+    private KeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon) {
+        // Legacy image icons using a single image will not support shadows
+        return KeyButtonDrawable.create(ctx, ctx.getDrawable(icon), null, false /* hasShadow */);
     }
 
     private TintedKeyButtonDrawable getDrawable(Context ctx, @DrawableRes int icon,
@@ -594,16 +585,8 @@
         super.setLayoutDirection(layoutDirection);
     }
 
-    private KeyButtonDrawable getBackIconWithAlt(boolean carMode, boolean landscape) {
-        return landscape
-                ? carMode ? mBackAltLandCarModeIcon : mBackIcon
-                : carMode ? mBackAltCarModeIcon : mBackIcon;
-    }
-
-    private KeyButtonDrawable getBackIcon(boolean carMode, boolean landscape) {
-        return landscape
-                ? carMode ? mBackLandCarModeIcon : mBackIcon
-                : carMode ? mBackCarModeIcon : mBackIcon;
+    private KeyButtonDrawable getBackIcon(boolean carMode) {
+        return carMode ? mBackCarModeIcon : mBackIcon;
     }
 
     public void setNavigationIconHints(int hints) {
@@ -643,12 +626,10 @@
         // to recent icon is not required.
         final boolean useAltBack =
                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        KeyButtonDrawable backIcon = useAltBack
-                ? getBackIconWithAlt(mUseCarModeUi, mVertical)
-                : getBackIcon(mUseCarModeUi, mVertical);
+        KeyButtonDrawable backIcon = getBackIcon(mUseCarModeUi);
+        orientBackButton(backIcon);
         KeyButtonDrawable homeIcon = mUseCarModeUi ? mHomeCarModeIcon : mHomeDefaultIcon;
         if (!mUseCarModeUi) {
-            orientBackButton(backIcon);
             orientHomeButton(homeIcon);
         }
         getHomeButton().setImageDrawable(homeIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NoisyVelocityTracker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NoisyVelocityTracker.java
index 214dda2..9c9b929 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NoisyVelocityTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NoisyVelocityTracker.java
@@ -60,7 +60,7 @@
         if (mEventBuf.size() == MAX_EVENTS) {
             mEventBuf.remove();
         }
-        mEventBuf.add(new MotionEventCopy(event.getX(), event.getY(), event.getEventTime()));
+        mEventBuf.add(new MotionEventCopy(event.getRawX(), event.getRawY(), event.getEventTime()));
     }
 
     public void computeCurrentVelocity(int units) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 7812a14..7b9dc0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -227,13 +227,7 @@
     }
 
     private void trackMovement(MotionEvent event) {
-        // Add movement to velocity tracker using raw screen X and Y coordinates instead
-        // of window coordinates because the window frame may be moving at the same time.
-        float deltaX = event.getRawX() - event.getX();
-        float deltaY = event.getRawY() - event.getY();
-        event.offsetLocation(deltaX, deltaY);
         if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
-        event.offsetLocation(-deltaX, -deltaY);
     }
 
     public void setTouchDisabled(boolean disabled) {
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
new file mode 100644
index 0000000..9c80cb7
--- /dev/null
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.telephony.data;
+
+import android.telephony.data.IQualifiedNetworksServiceCallback;
+
+/**
+ * {@hide}
+ */
+interface IQualifiedNetworksService
+{
+    oneway void createNetworkAvailabilityUpdater(int slotId, IQualifiedNetworksServiceCallback callback);
+    oneway void removeNetworkAvailabilityUpdater(int slotId);
+}
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
new file mode 100644
index 0000000..e8e1f01
--- /dev/null
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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.telephony.data;
+
+/**
+ * The qualified networks service call back interface
+ * @hide
+ */
+oneway interface IQualifiedNetworksServiceCallback
+{
+    void onQualifiedNetworkTypesChanged(int apnType, in int[] qualifiedNetworkTypesList);
+}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
new file mode 100644
index 0000000..bb89f19
--- /dev/null
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 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.telephony.data;
+
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Rlog;
+import android.telephony.data.ApnSetting.ApnType;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Base class of the qualified networks service. Services that extend QualifiedNetworksService must
+ * register the service in their AndroidManifest to be detected by the framework. They must be
+ * protected by the permission "android.permission.BIND_TELEPHONY_QUALIFIED_NETWORKS_SERVICE".
+ * The qualified networks service definition in the manifest must follow the following format:
+ * ...
+ * <service android:name=".xxxQualifiedNetworksService"
+ *     android:permission="android.permission.BIND_TELEPHONY_QUALIFIED_NETWORKS_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.data.QualifiedNetworksService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class QualifiedNetworksService extends Service {
+    private static final String TAG = QualifiedNetworksService.class.getSimpleName();
+
+    public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE =
+            "android.telephony.data.QualifiedNetworksService";
+
+    private static final int QNS_CREATE_NETWORK_AVAILABILITY_UPDATER                = 1;
+    private static final int QNS_REMOVE_NETWORK_AVAILABILITY_UPDATER                = 2;
+    private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_UPDATERS           = 3;
+    private static final int QNS_UPDATE_QUALIFIED_NETWORKS                          = 4;
+
+    private final HandlerThread mHandlerThread;
+
+    private final QualifiedNetworksServiceHandler mHandler;
+
+    private final SparseArray<NetworkAvailabilityUpdater> mUpdaters = new SparseArray<>();
+
+    /** @hide */
+    @VisibleForTesting
+    public final IQualifiedNetworksServiceWrapper mBinder = new IQualifiedNetworksServiceWrapper();
+
+    /**
+     * The abstract class of the network availability updater implementation. The vendor qualified
+     * network service must extend this class to report the available networks for data
+     * connection setup. Note that each instance of network availability updater is associated with
+     * one physical SIM slot.
+     */
+    public abstract class NetworkAvailabilityUpdater implements AutoCloseable {
+        private final int mSlotIndex;
+
+        private IQualifiedNetworksServiceCallback mCallback;
+
+        /**
+         * Qualified networks for each APN type. Key is the {@link ApnType}, value is the array
+         * of available networks.
+         */
+        private SparseArray<int[]> mQualifiedNetworkTypesList = new SparseArray<>();
+
+        /**
+         * Constructor
+         * @param slotIndex SIM slot index the network availability updater associated with.
+         */
+        public NetworkAvailabilityUpdater(int slotIndex) {
+            mSlotIndex = slotIndex;
+        }
+
+        /**
+         * @return SIM slot index the network availability updater associated with.
+         */
+        public final int getSlotIndex() {
+            return mSlotIndex;
+        }
+
+        private void registerForQualifiedNetworkTypesChanged(
+                IQualifiedNetworksServiceCallback callback) {
+            mCallback = callback;
+
+            // Force sending the qualified networks upon registered.
+            if (mCallback != null) {
+                for (int i = 0; i < mQualifiedNetworkTypesList.size(); i++) {
+                    try {
+                        mCallback.onQualifiedNetworkTypesChanged(
+                                mQualifiedNetworkTypesList.keyAt(i),
+                                mQualifiedNetworkTypesList.valueAt(i));
+                    } catch (RemoteException e) {
+                        loge("Failed to call onQualifiedNetworksChanged. " + e);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Update the qualified networks list. Network availability updater must invoke this method
+         * whenever the qualified networks changes. If this method is never invoked for certain
+         * APN type, then frameworks will always use the default (i.e. cellular) data and network
+         * service.
+         *
+         * @param apnType APN type of the qualified networks
+         * @param qualifiedNetworkTypes List of network types which are qualified for data
+         * connection setup for {@link @apnType} in the preferred order. Each element in the array
+         * is a {@link AccessNetworkType}. An empty list or null indicates no networks are qualified
+         * for data setup.
+         */
+        public final void updateQualifiedNetworkTypes(@ApnType int apnType,
+                                                      int[] qualifiedNetworkTypes) {
+            mHandler.obtainMessage(QNS_UPDATE_QUALIFIED_NETWORKS, mSlotIndex, apnType,
+                    qualifiedNetworkTypes).sendToTarget();
+        }
+
+        private void onUpdateQualifiedNetworkTypes(@ApnType int apnType,
+                                                   int[] qualifiedNetworkTypes) {
+            mQualifiedNetworkTypesList.put(apnType, qualifiedNetworkTypes);
+            if (mCallback != null) {
+                try {
+                    mCallback.onQualifiedNetworkTypesChanged(apnType, qualifiedNetworkTypes);
+                } catch (RemoteException e) {
+                    loge("Failed to call onQualifiedNetworksChanged. " + e);
+                }
+            }
+        }
+
+        /**
+         * Called when the qualified networks updater is removed. The extended class should
+         * implement this method to perform clean up works.
+         */
+        @Override
+        public abstract void close();
+    }
+
+    private class QualifiedNetworksServiceHandler extends Handler {
+        QualifiedNetworksServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            IQualifiedNetworksServiceCallback callback;
+            final int slotIndex = message.arg1;
+            NetworkAvailabilityUpdater updater = mUpdaters.get(slotIndex);
+
+            switch (message.what) {
+                case QNS_CREATE_NETWORK_AVAILABILITY_UPDATER:
+                    if (mUpdaters.get(slotIndex) != null) {
+                        loge("Network availability updater for slot " + slotIndex
+                                + " already existed.");
+                        return;
+                    }
+
+                    updater = createNetworkAvailabilityUpdater(slotIndex);
+                    if (updater != null) {
+                        mUpdaters.put(slotIndex, updater);
+
+                        callback = (IQualifiedNetworksServiceCallback) message.obj;
+                        updater.registerForQualifiedNetworkTypesChanged(callback);
+                    } else {
+                        loge("Failed to create network availability updater. slot index = "
+                                + slotIndex);
+                    }
+                    break;
+
+                case QNS_REMOVE_NETWORK_AVAILABILITY_UPDATER:
+                    if (updater != null) {
+                        updater.close();
+                        mUpdaters.remove(slotIndex);
+                    }
+                    break;
+
+                case QNS_REMOVE_ALL_NETWORK_AVAILABILITY_UPDATERS:
+                    for (int i = 0; i < mUpdaters.size(); i++) {
+                        updater = mUpdaters.get(i);
+                        if (updater != null) {
+                            updater.close();
+                        }
+                    }
+                    mUpdaters.clear();
+                    break;
+
+                case QNS_UPDATE_QUALIFIED_NETWORKS:
+                    if (updater == null) break;
+                    updater.onUpdateQualifiedNetworkTypes(message.arg2, (int[]) message.obj);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public QualifiedNetworksService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new QualifiedNetworksServiceHandler(mHandlerThread.getLooper());
+        log("Qualified networks service created");
+    }
+
+    /**
+     * Create the instance of {@link NetworkAvailabilityUpdater}. Vendor qualified network service
+     * must override this method to facilitate the creation of {@link NetworkAvailabilityUpdater}
+     * instances. The system will call this method after binding the qualified networks service for
+     * each active SIM slot index.
+     *
+     * @param slotIndex SIM slot index the qualified networks service associated with.
+     * @return Qualified networks service instance
+     */
+    public abstract NetworkAvailabilityUpdater createNetworkAvailabilityUpdater(int slotIndex);
+
+    /** @hide */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !QUALIFIED_NETWORKS_SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+        return mBinder;
+    }
+
+    /** @hide */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(QNS_REMOVE_ALL_NETWORK_AVAILABILITY_UPDATERS).sendToTarget();
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quit();
+    }
+
+    /**
+     * A wrapper around IQualifiedNetworksService that forwards calls to implementations of
+     * {@link QualifiedNetworksService}.
+     */
+    private class IQualifiedNetworksServiceWrapper extends IQualifiedNetworksService.Stub {
+        @Override
+        public void createNetworkAvailabilityUpdater(int slotIndex,
+                                                     IQualifiedNetworksServiceCallback callback) {
+            mHandler.obtainMessage(QNS_CREATE_NETWORK_AVAILABILITY_UPDATER, slotIndex, 0,
+                    callback).sendToTarget();
+        }
+
+        @Override
+        public void removeNetworkAvailabilityUpdater(int slotIndex) {
+            mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_UPDATER, slotIndex, 0)
+                    .sendToTarget();
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}