Merge "Define EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE in DctConstants"
diff --git a/api/current.txt b/api/current.txt
index 1f29fcb0..b691ef3 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -43032,7 +43032,8 @@
 
 package android.telephony.emergency {
 
-  public final class EmergencyNumber implements android.os.Parcelable {
+  public final class EmergencyNumber implements java.lang.Comparable android.os.Parcelable {
+    method public int compareTo(android.telephony.emergency.EmergencyNumber);
     method public int describeContents();
     method public java.lang.String getCountryIso();
     method public int getEmergencyNumberSourceBitmask();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bbc51ce..aec4520 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3932,18 +3932,31 @@
     }
 
      /**
-     * Indicate Hearing Aid connection state change.
+     * Indicate Hearing Aid connection state change and eventually suppress
+     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
      * @param device Bluetooth device connected/disconnected
      * @param state new connection state (BluetoothProfile.STATE_xxx)
+     * @param musicDevice Default get system volume for the connecting device.
+     * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or
+     * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
+     * @param suppressNoisyIntent if true the
+     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     * @return a delay in ms that the caller should wait before broadcasting
+     * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
      * {@hide}
      */
-    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) {
+    public int setBluetoothHearingAidDeviceConnectionState(
+                BluetoothDevice device, int state, boolean suppressNoisyIntent,
+                int musicDevice) {
         final IAudioService service = getService();
+        int delay = 0;
         try {
-            service.setHearingAidDeviceConnectionState(device, state);
+            delay = service.setBluetoothHearingAidDeviceConnectionState(device,
+                state, suppressNoisyIntent, musicDevice);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+        return delay;
     }
 
      /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 569db16..abd6411 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -151,8 +151,6 @@
     void setWiredDeviceConnectionState(int type, int state, String address, String name,
             String caller);
 
-    void setHearingAidDeviceConnectionState(in BluetoothDevice device, int state);
-
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
     void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
@@ -210,6 +208,9 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
+    int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
+            int state, boolean suppressNoisyIntent, int musicDevice);
+
     int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
             int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bf69437..40f1437 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4661,22 +4661,15 @@
         }
     }
 
-    @Override
-    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
-    {
-        mDeviceLogger.log((new AudioEventLogger.StringEvent(
-                "setHearingAidDeviceConnectionState state=" + state
-                        + " addr=" + device.getAddress())).printLog(TAG));
-
-        setBluetoothHearingAidDeviceConnectionState(
-                device, state,  false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
-    }
-
     public int setBluetoothHearingAidDeviceConnectionState(
             BluetoothDevice device, int state, boolean suppressNoisyIntent,
             int musicDevice)
     {
         int delay;
+        mDeviceLogger.log((new AudioEventLogger.StringEvent(
+                "setHearingAidDeviceConnectionState state=" + state
+                            + " addr=" + device.getAddress()
+                            + " supprNoisy=" + suppressNoisyIntent)).printLog(TAG));
         synchronized (mConnectedDevices) {
             if (!suppressNoisyIntent) {
                 int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
@@ -5866,6 +5859,7 @@
                                    address));
         sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0);
+        setCurrentAudioRouteNameIfPossible(name);
     }
 
     private void onSendBecomingNoisyIntent() {
@@ -5887,7 +5881,7 @@
         mConnectedDevices.remove(
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // Remove A2DP routes as well
-        setCurrentAudioRouteName(null);
+        setCurrentAudioRouteNameIfPossible(null);
         if (mDockAddress == address) {
             mDockAddress = null;
         }
@@ -5954,6 +5948,10 @@
                                    address));
         sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                 AudioSystem.DEVICE_OUT_HEARING_AID, 0, null, 0);
+        sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
+                            AudioSystem.DEVICE_OUT_HEARING_AID, 0,
+                            mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+        setCurrentAudioRouteNameIfPossible(name);
     }
 
     // must be called synchronized on mConnectedDevices
@@ -5963,7 +5961,7 @@
         mConnectedDevices.remove(
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
         // Remove Hearing Aid routes as well
-        setCurrentAudioRouteName(null);
+        setCurrentAudioRouteNameIfPossible(null);
     }
 
     // must be called synchronized on mConnectedDevices
@@ -6008,7 +6006,6 @@
                 } else {
                     makeA2dpDeviceUnavailableNow(address);
                 }
-                setCurrentAudioRouteName(null);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                 if (btDevice.isBluetoothDock()) {
                     // this could be a reconnection after a transient disconnection
@@ -6032,7 +6029,6 @@
                 }
                 makeA2dpDeviceAvailable(address, btDevice.getName(),
                         "onSetA2dpSinkConnectionState");
-                setCurrentAudioRouteName(btDevice.getAliasName());
             }
         }
     }
@@ -6084,25 +6080,35 @@
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                 makeHearingAidDeviceUnavailable(address);
-                setCurrentAudioRouteName(null);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                 makeHearingAidDeviceAvailable(address, btDevice.getName(),
                         "onSetHearingAidConnectionState");
-                setCurrentAudioRouteName(btDevice.getAliasName());
             }
         }
     }
 
-    private void setCurrentAudioRouteName(String name){
+    private void setCurrentAudioRouteNameIfPossible(String name) {
         synchronized (mCurAudioRoutes) {
             if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
-                mCurAudioRoutes.bluetoothName = name;
-                sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
-                        SENDMSG_NOOP, 0, 0, null, 0);
+                if (name != null || !isCurrentDeviceConnected()) {
+                    mCurAudioRoutes.bluetoothName = name;
+                    sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+                            SENDMSG_NOOP, 0, 0, null, 0);
+                }
             }
         }
     }
 
+    private boolean isCurrentDeviceConnected() {
+        for (int i = 0; i < mConnectedDevices.size(); i++) {
+            DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
+            if (TextUtils.equals(deviceSpec.mDeviceName, mCurAudioRoutes.bluetoothName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
     {
         if (DEBUG_DEVICES) {
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index d6a08543..bdba8c8 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -33,7 +33,7 @@
  * A parcelable class that wraps and retrieves the information of number, service category(s) and
  * country code for a specific emergency number.
  */
-public final class EmergencyNumber implements Parcelable {
+public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> {
 
     private static final String LOG_TAG = "EmergencyNumber";
 
@@ -235,20 +235,22 @@
     }
 
     /**
-     * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
-     * the emergency number.
+     * Returns the bitmask of emergency service categories of the emergency number.
      *
-     * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+     * @return bitmask of the emergency service categories
      */
     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
         return mEmergencyServiceCategoryBitmask;
     }
 
     /**
-     * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
-     * number.
+     * Returns the emergency service categories of the emergency number.
      *
-     * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+     * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
+     * all categories.
+     *
+     * @return a list of the emergency service categories
      */
     public List<Integer> getEmergencyServiceCategories() {
         List<Integer> categories = new ArrayList<>();
@@ -276,34 +278,37 @@
     }
 
     /**
-     * Checks if the emergency number is in the specified emergency service category(s)
-     * {@link EmergencyServiceCategories}.
+     * Checks if the emergency number is in the supplied emergency service category(s).
+     *
+     * @param categories - the supplied emergency service categories
      *
      * @return {@code true} if the emergency number is in the specified emergency service
-     * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
-     *
-     * @param categories - emergency service categories {@link EmergencyServiceCategories}
+     * category(s) or if its emergency service category is
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
      */
     public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
         if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
             return serviceUnspecified();
         }
+        if (serviceUnspecified()) {
+            return true;
+        }
         return (mEmergencyServiceCategoryBitmask & categories) == categories;
     }
 
     /**
-     * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+     * Returns the bitmask of the sources of the emergency number.
      *
-     * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+     * @return bitmask of the emergency number sources
      */
     public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
         return mEmergencyNumberSourceBitmask;
     }
 
     /**
-     * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+     * Returns a list of sources of the emergency number.
      *
-     * @return a list of {@link EmergencyNumberSources}
+     * @return a list of emergency number sources
      */
     public List<Integer> getEmergencyNumberSources() {
         List<Integer> sources = new ArrayList<>();
@@ -316,13 +321,12 @@
     }
 
     /**
-     * Checks if the emergency number is from the specified emergency number source(s)
-     * {@link EmergencyNumberSources}.
+     * Checks if the emergency number is from the specified emergency number source(s).
      *
      * @return {@code true} if the emergency number is from the specified emergency number
-     * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+     * source(s); {@code false} otherwise.
      *
-     * @param sources - {@link EmergencyNumberSources}
+     * @param sources - the supplied emergency number sources
      */
     public boolean isFromSources(@EmergencyNumberSources int sources) {
         return (mEmergencyNumberSourceBitmask & sources) == sources;
@@ -359,6 +363,62 @@
         return (o == this || toString().equals(o.toString()));
     }
 
+    /**
+     * Calculate the score for display priority.
+     *
+     * A higher display priority score means the emergency number has a higher display priority.
+     * The score is higher if the source is defined for a higher display priority.
+     *
+     * The priority of sources are defined as follows:
+     *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
+     *     EMERGENCY_NUMBER_SOURCE_SIM >
+     *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
+     *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+     *
+     */
+    private int getDisplayPriorityScore() {
+        int score = 0;
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
+            score += 1 << 4;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
+            score += 1 << 3;
+        }
+        // TODO add a score if the number comes from Google's emergency number database
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
+            score += 1 << 1;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
+            score += 1 << 0;
+        }
+        return score;
+    }
+
+    /**
+     * Compare the display priority for this emergency number and the supplied emergency number.
+     *
+     * @param emergencyNumber the supplied emergency number
+     * @return a negative value if the supplied emergency number has a lower display priority;
+     *         a positive value if the supplied emergency number has a higher display priority;
+     *         0 if both have equal display priority.
+     */
+    @Override
+    public int compareTo(EmergencyNumber emergencyNumber) {
+        if (this.getDisplayPriorityScore()
+                > emergencyNumber.getDisplayPriorityScore()) {
+            return -1;
+        } else if (this.getDisplayPriorityScore()
+                < emergencyNumber.getDisplayPriorityScore()) {
+            return 1;
+        } else {
+            /**
+             * TODO if both numbers have the same display priority score, the number matches the
+             * Google's emergency number database has a higher display priority.
+             */
+            return 0;
+        }
+    }
+
     public static final Parcelable.Creator<EmergencyNumber> CREATOR =
             new Parcelable.Creator<EmergencyNumber>() {
         @Override