Killing CallList + crash fix

- Replacing UiCallList and subclass with simple HashMap in
  TelecomUiCallManager. TelecomUiCallManager.getOrCreateCallContainer
  now does simple map search. Removed synchronization since callbacks
  from InCallServiceImpl are on main thread AFAICT; double-checked with
  logging.
- OngoingCallFragment crash fix: Don't unset uiBluetoothMonitor since
  the fragment object can be re-used by TelecomActivity for next call.
  TelecomActivity only passes uiBluetoothMonitor the first time after
  construction.
- Fixing UiCall.disconnectClause -> UiCall.disconnectCause.

Bug: 37251324
Test: Made test calls, incoming, outgoing, conference.
Change-Id: I63f24cc9793b4b2c4547146a836205413f106c83
diff --git a/src/com/android/car/dialer/OngoingCallFragment.java b/src/com/android/car/dialer/OngoingCallFragment.java
index 9c32241..2eb659e 100644
--- a/src/com/android/car/dialer/OngoingCallFragment.java
+++ b/src/com/android/car/dialer/OngoingCallFragment.java
@@ -125,7 +125,6 @@
         mHandler = null;
         mUiCallManager = null;
         mLoadedNumber = null;
-        mUiBluetoothMonitor = null;
     }
 
     @Override
@@ -297,7 +296,7 @@
         // Toggle the visibility between the active call controls, ringing call controls,
         // and no controls.
         CharSequence disconnectCauseLabel = mLastRemovedCall == null ?
-                null : mLastRemovedCall.getDisconnectClause();
+                null : mLastRemovedCall.getDisconnectCause();
         if (mPrimaryCall == null && !TextUtils.isEmpty(disconnectCauseLabel)) {
             closeDialpad();
             setStateText(disconnectCauseLabel);
diff --git a/src/com/android/car/dialer/telecom/UiCall.java b/src/com/android/car/dialer/telecom/UiCall.java
index 523d893..7b98c64 100644
--- a/src/com/android/car/dialer/telecom/UiCall.java
+++ b/src/com/android/car/dialer/telecom/UiCall.java
@@ -26,7 +26,7 @@
     private int mState;
     private boolean mHasParent;
     private String mNumber;
-    private CharSequence mDisconnectClause;
+    private CharSequence mDisconnectCause;
     private boolean mHasChildren;
     private Uri mGatewayInfoOriginalAddress;
     private long connectTimeMillis;
@@ -71,12 +71,12 @@
         mNumber = number;
     }
 
-    public CharSequence getDisconnectClause() {
-        return mDisconnectClause;
+    public CharSequence getDisconnectCause() {
+        return mDisconnectCause;
     }
 
-    public void setDisconnectClause(CharSequence disconnectClause) {
-        mDisconnectClause = disconnectClause;
+    public void setDisconnectCause(CharSequence disconnectCause) {
+        mDisconnectCause = disconnectCause;
     }
 
     public Uri getGatewayInfoOriginalAddress() {
diff --git a/src/com/android/car/dialer/telecom/UiCallList.java b/src/com/android/car/dialer/telecom/UiCallList.java
deleted file mode 100644
index cb730f1..0000000
--- a/src/com/android/car/dialer/telecom/UiCallList.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car.dialer.telecom;
-
-import android.support.v4.util.Pair;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Holds a list of {@link UiCall} and implements convenient mapping from underlying
- * {@code Call} class to {@link UiCall}.
- */
-public abstract class UiCallList<C> {
-    private Map<C, UiCall> mCallToCallInfoMap = new HashMap<>();
-    private SparseArray<Pair<UiCall, C>> mIdToCallMap = new SparseArray<>();
-
-    /**
-     * Creates or gets existing {@code CallInfo} instance for a given key
-     */
-    public UiCall getOrCreate(C call) {
-        if (call == null) {
-            return null;
-        }
-        UiCall uiCall = mCallToCallInfoMap.get(call);
-        if (uiCall == null) {
-            uiCall = createUiCall(call);
-            mCallToCallInfoMap.put(call, uiCall);
-            mIdToCallMap.append(uiCall.getId(), new Pair<>(uiCall, call));
-        }
-        return uiCall;
-    }
-
-    /**
-     * Returns a list of existing {@code CallInfo}.
-     */
-    public List<UiCall> getCalls() {
-        return new ArrayList<>(mCallToCallInfoMap.values());
-    }
-
-    /**
-     * Clears a list.
-     */
-    public void clearCalls() {
-        mCallToCallInfoMap.clear();
-        mIdToCallMap.clear();
-    }
-
-    /**
-     * Removes {@code CallInfo} by given key.
-     */
-    public void remove(UiCall uiCall) {
-        int callId = uiCall.getId();
-        C call = getCall(callId);
-        if (mCallToCallInfoMap.containsKey(call)) {
-            mCallToCallInfoMap.remove(call);
-        }
-        mIdToCallMap.remove(callId);
-    }
-
-    protected C getCall(int callId) {
-        return mIdToCallMap.get(callId).second;
-    }
-
-    public UiCall getUiCall(int callId) {
-        return mIdToCallMap.get(callId).first;
-    }
-
-    abstract protected UiCall createUiCall(C call);
-}
diff --git a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallList.java b/src/com/android/car/dialer/telecom/embedded/TelecomUiCallList.java
deleted file mode 100644
index 17e1df8..0000000
--- a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallList.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car.dialer.telecom.embedded;
-
-import com.android.car.dialer.telecom.UiCall;
-import com.android.car.dialer.telecom.UiCallList;
-
-import android.telecom.Call;
-import android.telecom.DisconnectCause;
-import android.telecom.GatewayInfo;
-import android.util.Log;
-
-/**
- * Represents a list of {@link UiCall} for underlying {@link android.telecom.Call}.
- */
-public class TelecomUiCallList extends UiCallList<Call> {
-    private final static String TAG = "Em.UiCallList";
-
-    private volatile static int nextCarPhoneCallId = 0;
-
-    private int getNewCarPhoneCallId() {
-        return nextCarPhoneCallId++;
-    }
-
-    @Override
-    protected UiCall createUiCall(Call telecomCall) {
-        int id = getNewCarPhoneCallId();
-
-        UiCall uiCall = new UiCall(id);
-        updateCallContainerFromTelecom(uiCall, telecomCall);
-        return uiCall;
-    }
-
-    static void updateCallContainerFromTelecom(UiCall uiCall, Call telecomCall) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "updateCallContainerFromTelecom: call: " + uiCall + ", telecomCall: "
-                    + telecomCall);
-        }
-
-        uiCall.setState(telecomCall.getState());
-        uiCall.setHasChildren(!telecomCall.getChildren().isEmpty());
-        uiCall.setHasParent(telecomCall.getParent() != null);
-
-        Call.Details details = telecomCall.getDetails();
-        if (details != null) {
-            uiCall.setConnectTimeMillis(details.getConnectTimeMillis());
-
-            DisconnectCause cause = details.getDisconnectCause();
-            CharSequence causeLabel = cause == null ? null : cause.getLabel();
-            uiCall.setDisconnectClause(causeLabel == null ? null : causeLabel.toString());
-
-            GatewayInfo gatewayInfo = details.getGatewayInfo();
-            uiCall.setGatewayInfoOriginalAddress(
-                    gatewayInfo == null ? null : gatewayInfo.getOriginalAddress());
-
-            String number = "";
-            if (gatewayInfo != null) {
-                number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart();
-            } else if (details.getHandle() != null) {
-                number = details.getHandle().getSchemeSpecificPart();
-            }
-            uiCall.setNumber(number);
-        }
-    }
-
-    public Call getTelecomCall(UiCall uiCall) {
-        return uiCall != null ? getCall(uiCall.getId()) : null;
-    }
-}
diff --git a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java b/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java
index f81dec3..357512f 100644
--- a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java
+++ b/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java
@@ -27,12 +27,17 @@
 import android.telecom.Call;
 import android.telecom.Call.Details;
 import android.telecom.CallAudioState;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
 import android.telecom.InCallService.VideoCall;
 import android.telecom.TelecomManager;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -40,11 +45,14 @@
  */
 public class TelecomUiCallManager extends UiCallManager {
 
-    private static final String TAG = "Em.TelecomMgrImpl";
+    private static final String TAG = "Em.TelecomUiCallMgr";
+
+    // Used to assign id's to UiCall objects as they're created.
+    private static int nextCarPhoneCallId = 0;
 
     private TelecomManager mTelecomManager;
     private InCallServiceImpl mInCallService;
-    private TelecomUiCallList mCallList = new TelecomUiCallList();
+    private Map<UiCall, Call> mCallMapping = new HashMap<>();
 
     private List<CallListener> mCallListeners = new CopyOnWriteArrayList<>();
 
@@ -62,7 +70,7 @@
             mContext.unbindService(mInCallServiceConnection);
             mInCallService = null;
         }
-        mCallList.clearCalls();
+        mCallMapping.clear();
     }
 
     @Override
@@ -97,7 +105,7 @@
             Log.d(TAG, "answerCall: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.answer(0);
         }
@@ -110,7 +118,7 @@
                     + "textMessage: " + textMessage);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.reject(rejectWithMessage, textMessage);
         }
@@ -122,7 +130,7 @@
             Log.d(TAG, "disconnectCall: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.disconnect();
         }
@@ -130,7 +138,7 @@
 
     @Override
     public List<UiCall> getCalls() {
-        return mCallList.getCalls();
+        return new ArrayList<>(mCallMapping.keySet());
     }
 
     @Override
@@ -189,7 +197,7 @@
             Log.d(TAG, "holdCall: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.hold();
         }
@@ -201,7 +209,7 @@
             Log.d(TAG, "unholdCall: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.unhold();
         }
@@ -213,7 +221,7 @@
             Log.d(TAG, "playDtmfTone: call: " + uiCall + ", digit: " + digit);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.playDtmfTone(digit);
         }
@@ -225,7 +233,7 @@
             Log.d(TAG, "stopDtmfTone: call: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.stopDtmfTone();
         }
@@ -237,7 +245,7 @@
             Log.d(TAG, "postDialContinue: call: " + uiCall + ", proceed: " + proceed);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.postDialContinue(proceed);
         }
@@ -249,8 +257,8 @@
             Log.d(TAG, "conference: call: " + uiCall + ", otherCall: " + otherUiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
-        Call otherTelecomCall = mCallList.getTelecomCall(otherUiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
+        Call otherTelecomCall = mCallMapping.get(otherUiCall);
         if (telecomCall != null) {
             telecomCall.conference(otherTelecomCall);
         }
@@ -262,7 +270,7 @@
             Log.d(TAG, "splitFromConference: call: " + uiCall);
         }
 
-        Call telecomCall = mCallList.getTelecomCall(uiCall);
+        Call telecomCall = mCallMapping.get(uiCall);
         if (telecomCall != null) {
             telecomCall.splitFromConference();
         }
@@ -292,7 +300,7 @@
     private void doTelecomCallRemoved(Call telecomCall) {
         UiCall uiCall = getOrCreateCallContainer(telecomCall);
 
-        mCallList.remove(uiCall);
+        mCallMapping.remove(uiCall);
 
         for (CallListener listener : mCallListeners) {
             listener.onCallRemoved(uiCall);
@@ -377,7 +385,7 @@
             TelecomUiCallManager manager = mCarTelecomMangerRef.get();
             UiCall uiCall = mCallContainerRef.get();
             if (manager != null && uiCall != null) {
-                TelecomUiCallList.updateCallContainerFromTelecom(uiCall, telecomCall);
+                updateCallContainerFromTelecom(uiCall, telecomCall);
                 manager.onCallUpdated(uiCall);
             }
         }
@@ -430,9 +438,50 @@
     };
 
     private UiCall getOrCreateCallContainer(Call telecomCall) {
-        synchronized (mCallList) {
-            return mCallList.getOrCreate(telecomCall);
+        for (Map.Entry<UiCall, Call> entry : mCallMapping.entrySet()) {
+            if (entry.getValue() == telecomCall) {
+                return entry.getKey();
+            }
         }
+
+        UiCall uiCall = new UiCall(nextCarPhoneCallId++);
+        updateCallContainerFromTelecom(uiCall, telecomCall);
+        mCallMapping.put(uiCall, telecomCall);
+        return uiCall;
+    }
+
+    private static void updateCallContainerFromTelecom(UiCall uiCall, Call telecomCall) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "updateCallContainerFromTelecom: call: " + uiCall + ", telecomCall: "
+                    + telecomCall);
+        }
+
+        uiCall.setState(telecomCall.getState());
+        uiCall.setHasChildren(!telecomCall.getChildren().isEmpty());
+        uiCall.setHasParent(telecomCall.getParent() != null);
+
+        Call.Details details = telecomCall.getDetails();
+        if (details == null) {
+            return;
+        }
+
+        uiCall.setConnectTimeMillis(details.getConnectTimeMillis());
+
+        DisconnectCause cause = details.getDisconnectCause();
+        uiCall.setDisconnectCause(cause == null ? null : cause.getLabel());
+
+        GatewayInfo gatewayInfo = details.getGatewayInfo();
+        uiCall.setGatewayInfoOriginalAddress(
+                gatewayInfo == null ? null : gatewayInfo.getOriginalAddress());
+
+        String number = "";
+        if (gatewayInfo != null) {
+            number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart();
+        } else if (details.getHandle() != null) {
+            number = details.getHandle().getSchemeSpecificPart();
+        }
+        uiCall.setNumber(number);
+
     }
 
     private CallAudioState getCallAudioStateOrNull() {