AudioService: Migrate index map to SparseIntArray.

 - Use SparseIntArray instead of a generic hash map.
 - Avoids unnecessary boxing/unboxing.
 - Avoids unnecessary allocation for iterator temp objects.
 - Provides deterministic dumping (by ascending device order).

Change-Id: If5ca5edd94af90cfed65ecbbbc1f4929f50ab6e9
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 473f5db..6ce6ea9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.view.KeyEvent;
 import android.view.OrientationEventListener;
 import android.view.Surface;
@@ -118,7 +119,6 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * The implementation of the volume manager service.
@@ -1900,12 +1900,12 @@
                 if ((isPlatformVoice() || mHasVibrator) &&
                         mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
                     synchronized (VolumeStreamState.class) {
-                        Set set = mStreamStates[streamType].mIndex.entrySet();
-                        Iterator i = set.iterator();
-                        while (i.hasNext()) {
-                            Map.Entry entry = (Map.Entry)i.next();
-                            if ((Integer)entry.getValue() == 0) {
-                                entry.setValue(10);
+                        SparseIntArray indexMap = mStreamStates[streamType].mIndexMap;
+                        for (int i = 0; i < indexMap.size(); i++) {
+                            int device = indexMap.keyAt(i);
+                            int value = indexMap.valueAt(i);
+                            if (value == 0) {
+                                indexMap.put(device, 10);
                             }
                         }
                         // Persist volume for stream ring when it is changed here
@@ -3489,8 +3489,7 @@
         private boolean mIsMuted;
         private String mVolumeIndexSettingName;
         private int mIndexMax;
-        private final ConcurrentHashMap<Integer, Integer> mIndex =
-                                            new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
+        private final SparseIntArray mIndexMap = new SparseIntArray(8);
         private final Intent mVolumeChanged;
 
         private VolumeStreamState(String settingName, int streamType) {
@@ -3521,7 +3520,7 @@
                 // force maximum volume on all streams if fixed volume property
                 // or master volume property is set
                 if (mUseFixedVolume || mUseMasterVolume) {
-                    mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+                    mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
                     return;
                 }
                 // do not read system stream volume from settings: this stream is always aliased
@@ -3535,7 +3534,7 @@
                             index = mIndexMax;
                         }
                     }
-                    mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+                    mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
                     return;
                 }
 
@@ -3560,7 +3559,7 @@
                         continue;
                     }
 
-                    mIndex.put(device, getValidIndex(10 * index));
+                    mIndexMap.put(device, getValidIndex(10 * index));
                 }
             }
         }
@@ -3591,11 +3590,8 @@
                 }
                 AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
                 // then apply device specific volumes
-                Set set = mIndex.entrySet();
-                Iterator i = set.iterator();
-                while (i.hasNext()) {
-                    Map.Entry entry = (Map.Entry)i.next();
-                    int device = ((Integer)entry.getKey()).intValue();
+                for (int i = 0; i < mIndexMap.size(); i++) {
+                    int device = mIndexMap.keyAt(i);
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         if (mIsMuted) {
                             index = 0;
@@ -3605,7 +3601,7 @@
                         {
                             index = (mIndexMax + 5)/10;
                         } else {
-                            index = ((Integer)entry.getValue() + 5)/10;
+                            index = (mIndexMap.valueAt(i) + 5)/10;
                         }
                         AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
                     }
@@ -3629,7 +3625,7 @@
                         index = mIndexMax;
                     }
                 }
-                mIndex.put(device, index);
+                mIndexMap.put(device, index);
 
                 changed = oldIndex != index;
                 if (changed) {
@@ -3664,12 +3660,12 @@
 
         public int getIndex(int device) {
             synchronized (VolumeStreamState.class) {
-                Integer index = mIndex.get(device);
-                if (index == null) {
+                int index = mIndexMap.get(device, -1);
+                if (index == -1) {
                     // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
-                    index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
+                    index = mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
                 }
-                return index.intValue();
+                return index;
             }
         }
 
@@ -3684,19 +3680,14 @@
                 // some devices are present in this stream state but not in source stream state
                 int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
                 index = rescaleIndex(index, srcStreamType, mStreamType);
-                Set set = mIndex.entrySet();
-                Iterator i = set.iterator();
-                while (i.hasNext()) {
-                    Map.Entry entry = (Map.Entry)i.next();
-                    entry.setValue(index);
+                for (int i = 0; i < mIndexMap.size(); i++) {
+                    mIndexMap.put(mIndexMap.keyAt(i), index);
                 }
                 // Now apply actual volume for devices in source stream state
-                set = srcStream.mIndex.entrySet();
-                i = set.iterator();
-                while (i.hasNext()) {
-                    Map.Entry entry = (Map.Entry)i.next();
-                    int device = ((Integer)entry.getKey()).intValue();
-                    index = ((Integer)entry.getValue()).intValue();
+                SparseIntArray srcMap = srcStream.mIndexMap;
+                for (int i = 0; i < srcMap.size(); i++) {
+                    int device = srcMap.keyAt(i);
+                    index = srcMap.valueAt(i);
                     index = rescaleIndex(index, srcStreamType, mStreamType);
 
                     setIndex(index, device);
@@ -3706,11 +3697,8 @@
 
         public void setAllIndexesToMax() {
             synchronized (VolumeStreamState.class) {
-                Set set = mIndex.entrySet();
-                Iterator i = set.iterator();
-                while (i.hasNext()) {
-                    Map.Entry entry = (Map.Entry)i.next();
-                    entry.setValue(mIndexMax);
+                for (int i = 0; i < mIndexMap.size(); i++) {
+                    mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
                 }
             }
         }
@@ -3750,15 +3738,12 @@
             synchronized (VolumeStreamState.class) {
                 // ignore settings for fixed volume devices: volume should always be at max or 0
                 if (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) {
-                    Set set = mIndex.entrySet();
-                    Iterator i = set.iterator();
-                    while (i.hasNext()) {
-                        Map.Entry entry = (Map.Entry)i.next();
-                        int device = ((Integer)entry.getKey()).intValue();
-                        int index = ((Integer)entry.getValue()).intValue();
+                    for (int i = 0; i < mIndexMap.size(); i++) {
+                        int device = mIndexMap.keyAt(i);
+                        int index = mIndexMap.valueAt(i);
                         if (((device & mFullVolumeDevices) != 0)
                                 || (((device & mFixedVolumeDevices) != 0) && index != 0)) {
-                            entry.setValue(mIndexMax);
+                            mIndexMap.put(device, mIndexMax);
                         }
                         applyDeviceVolume_syncVSS(device);
                     }
@@ -3782,11 +3767,11 @@
             pw.print("   Max: ");
             pw.println((mIndexMax + 5) / 10);
             pw.print("   Current: ");
-            Set set = mIndex.entrySet();
-            Iterator i = set.iterator();
-            while (i.hasNext()) {
-                Map.Entry entry = (Map.Entry)i.next();
-                final int device = (Integer) entry.getKey();
+            for (int i = 0; i < mIndexMap.size(); i++) {
+                if (i > 0) {
+                    pw.print(", ");
+                }
+                final int device = mIndexMap.keyAt(i);
                 pw.print(Integer.toHexString(device));
                 final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
                         : AudioSystem.getOutputDeviceName(device);
@@ -3796,11 +3781,8 @@
                     pw.print(")");
                 }
                 pw.print(": ");
-                final int index = (((Integer) entry.getValue()) + 5) / 10;
+                final int index = (mIndexMap.valueAt(i) + 5) / 10;
                 pw.print(index);
-                if (i.hasNext()) {
-                    pw.print(", ");
-                }
             }
         }
     }