13d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer/*
23d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * Copyright (C) 2015 The Android Open Source Project
33d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer *
43d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License");
53d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * you may not use this file except in compliance with the License.
63d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * You may obtain a copy of the License at
73d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer *
83d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer *      http://www.apache.org/licenses/LICENSE-2.0
93d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer *
103d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * Unless required by applicable law or agreed to in writing, software
113d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS,
123d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * See the License for the specific language governing permissions and
143d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * limitations under the License.
153d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer */
163d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerpackage com.android.car.dialer.telecom;
173d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
18684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.bluetooth.BluetoothAdapter;
19684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.bluetooth.BluetoothDevice;
20684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.bluetooth.BluetoothHeadsetClient;
21684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.bluetooth.BluetoothHeadsetClientCall;
22684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.bluetooth.BluetoothProfile;
236ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.content.ComponentName;
243d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.content.Context;
256ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.content.Intent;
266ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.content.ServiceConnection;
273d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.database.Cursor;
286ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.net.Uri;
296ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.os.IBinder;
303d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.provider.CallLog;
313d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.telecom.Call;
326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.telecom.CallAudioState;
33684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.telecom.CallAudioState.CallAudioRoute;
346ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.telecom.DisconnectCause;
356ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.telecom.GatewayInfo;
366ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.telecom.InCallService;
37684d175b191db416438ba00355f20d16eb79cef3Yuzhouimport android.telecom.PhoneAccountHandle;
386ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport android.telecom.TelecomManager;
393d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.telephony.TelephonyManager;
403d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.text.TextUtils;
413d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport android.util.Log;
42a16cb24e5b5690af55597ad5c8540a7c90ae146aSrinivas Visvanathan
43bb51ff67f2973488079684ca58924a1c4b712dabJohnny Zhouimport com.android.car.dialer.CallListener;
443d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport com.android.car.dialer.R;
453d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport java.lang.ref.WeakReference;
473d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport java.util.ArrayList;
483d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport java.util.Calendar;
493d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport java.util.Collections;
503d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport java.util.Comparator;
516ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport java.util.HashMap;
523d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyerimport java.util.List;
536ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport java.util.Map;
546ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanimport java.util.concurrent.CopyOnWriteArrayList;
553d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
563d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer/**
573d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer * The entry point for all interactions between UI and telecom.
583d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer */
596ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathanpublic class UiCallManager {
603d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    private static String TAG = "Em.TelecomMgr";
613d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
62684d175b191db416438ba00355f20d16eb79cef3Yuzhou    private static final String HFP_CLIENT_CONNECTION_SERVICE_CLASS_NAME
63684d175b191db416438ba00355f20d16eb79cef3Yuzhou            = "com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService";
643d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    // Rate limit how often you can place outgoing calls.
653d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    private static final long MIN_TIME_BETWEEN_CALLS_MS = 3000;
663d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    private static final List<Integer> sCallStateRank = new ArrayList<>();
670ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    private static UiCallManager sUiCallManager;
683d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
696ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    // Used to assign id's to UiCall objects as they're created.
706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private static int nextCarPhoneCallId = 0;
713d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
723d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    static {
733d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        // States should be added from lowest rank to highest
743d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_DISCONNECTED);
753d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_DISCONNECTING);
763d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_NEW);
773d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_CONNECTING);
783d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_SELECT_PHONE_ACCOUNT);
793d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_HOLDING);
803d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_ACTIVE);
813d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_DIALING);
823d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        sCallStateRank.add(Call.STATE_RINGING);
833d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
843d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
856ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private Context mContext;
866ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private TelephonyManager mTelephonyManager;
876ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private long mLastPlacedCallTimeMs;
883d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
896ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private TelecomManager mTelecomManager;
906ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private InCallServiceImpl mInCallService;
91684d175b191db416438ba00355f20d16eb79cef3Yuzhou    private BluetoothHeadsetClient mBluetoothHeadsetClient;
926ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private final Map<UiCall, Call> mCallMapping = new HashMap<>();
936ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private final List<CallListener> mCallListeners = new CopyOnWriteArrayList<>();
943d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
950ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    /**
960ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * Initialized a globally accessible {@link UiCallManager} which can be retrieved by
970ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * {@link #get}. If this function is called a second time before calling {@link #tearDown()},
980ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * an exception will be thrown.
990ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     *
1000ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * @param applicationContext Application context.
1010ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     */
1020ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    public static UiCallManager init(Context applicationContext) {
1030ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        if (sUiCallManager == null) {
1040ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou            sUiCallManager = new UiCallManager(applicationContext);
1050ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        } else {
1060ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou            throw new IllegalStateException("UiCallManager has been initialized.");
1070ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        }
1080ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        return sUiCallManager;
1090ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    }
1100ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou
1110ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    /**
1120ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * Gets the global {@link UiCallManager} instance. Make sure
1130ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * {@link #init(Context)} is called before calling this method.
1140ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     */
1150ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    public static UiCallManager get() {
1160ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        if (sUiCallManager == null) {
1170ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou            throw new IllegalStateException(
1180ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou                    "Call UiCallManager.init(Context) before calling this function");
1190ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        }
1200ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        return sUiCallManager;
1210ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    }
1220ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou
1230ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    private UiCallManager(Context context) {
1243d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
1253d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "SetUp");
1263d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
1273d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
1283d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        mContext = context;
1293d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
1306ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
1316ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mTelecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
1326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Intent intent = new Intent(context, InCallServiceImpl.class);
1336ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        intent.setAction(InCallServiceImpl.ACTION_LOCAL_BIND);
1346ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        context.bindService(intent, mInCallServiceConnection, Context.BIND_AUTO_CREATE);
135684d175b191db416438ba00355f20d16eb79cef3Yuzhou
136684d175b191db416438ba00355f20d16eb79cef3Yuzhou        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
137684d175b191db416438ba00355f20d16eb79cef3Yuzhou        adapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
138684d175b191db416438ba00355f20d16eb79cef3Yuzhou            @Override
139684d175b191db416438ba00355f20d16eb79cef3Yuzhou            public void onServiceConnected(int profile, BluetoothProfile proxy) {
140684d175b191db416438ba00355f20d16eb79cef3Yuzhou                if (profile == BluetoothProfile.HEADSET_CLIENT) {
141684d175b191db416438ba00355f20d16eb79cef3Yuzhou                    mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
142684d175b191db416438ba00355f20d16eb79cef3Yuzhou                }
143684d175b191db416438ba00355f20d16eb79cef3Yuzhou            }
144684d175b191db416438ba00355f20d16eb79cef3Yuzhou
145684d175b191db416438ba00355f20d16eb79cef3Yuzhou            @Override
146684d175b191db416438ba00355f20d16eb79cef3Yuzhou            public void onServiceDisconnected(int profile) {
147684d175b191db416438ba00355f20d16eb79cef3Yuzhou            }
148684d175b191db416438ba00355f20d16eb79cef3Yuzhou        }, BluetoothProfile.HEADSET_CLIENT);
1493d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
1503d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
1516ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private final ServiceConnection mInCallServiceConnection = new ServiceConnection() {
1523d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
1536ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
1546ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onServiceConnected(ComponentName name, IBinder binder) {
1556ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (Log.isLoggable(TAG, Log.DEBUG)) {
1566ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                Log.d(TAG, "onServiceConnected: " + name + ", service: " + binder);
1576ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
1586ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mInCallService = ((InCallServiceImpl.LocalBinder) binder).getService();
1596ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mInCallService.registerCallback(mInCallServiceCallback);
1606ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
1616ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            // The InCallServiceImpl could be bound when we already have some active calls, let's
1626ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            // notify UI about these calls.
1636ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            for (Call telecomCall : mInCallService.getCalls()) {
1646ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                UiCall uiCall = doTelecomCallAdded(telecomCall);
1656ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                onStateChanged(uiCall, uiCall.getState());
1666ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
1676ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
1683d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
1696ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
1706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onServiceDisconnected(ComponentName name) {
1716ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (Log.isLoggable(TAG, Log.DEBUG)) {
1726ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                Log.d(TAG, "onServiceDisconnected: " + name);
1736ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
1746ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mInCallService.unregisterCallback(mInCallServiceCallback);
1756ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
1763d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
1776ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        private InCallServiceImpl.Callback mInCallServiceCallback =
1786ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                new InCallServiceImpl.Callback() {
1796ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    @Override
1806ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    public void onTelecomCallAdded(Call telecomCall) {
1816ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                        doTelecomCallAdded(telecomCall);
1826ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    }
1836ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
1846ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    @Override
1856ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    public void onTelecomCallRemoved(Call telecomCall) {
1866ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                        doTelecomCallRemoved(telecomCall);
1876ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    }
1886ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
1896ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    @Override
1906ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    public void onCallAudioStateChanged(CallAudioState audioState) {
1916ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                        doCallAudioStateChanged(audioState);
1926ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    }
1936ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                };
1946ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    };
1956ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
1960ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou    /**
1970ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * Tears down the {@link UiCallManager}. Calling this function will null out the global
1980ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * accessible {@link UiCallManager} instance. Remember to re-initialize the
1990ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     * {@link UiCallManager}.
2000ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou     */
2016ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void tearDown() {
2026ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (mInCallService != null) {
2036ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mContext.unbindService(mInCallServiceConnection);
2046ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mInCallService = null;
2056ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2066ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mCallMapping.clear();
2070ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        // Clear out the mContext reference to avoid memory leak.
2080ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        mContext = null;
2090ee4ae4c98ab4a94e1217399d4a71f6f6db87e99Yuzhou        sUiCallManager = null;
2106ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2113d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2126ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void addListener(CallListener listener) {
2136ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2146ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "addListener: " + listener);
2156ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2166ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mCallListeners.add(listener);
2176ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2183d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2196ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void removeListener(CallListener listener) {
2206ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2216ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "removeListener: " + listener);
2226ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2236ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mCallListeners.remove(listener);
2246ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2253d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2266ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    protected void placeCall(String number) {
2276ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2286ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "placeCall: " + number);
2296ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2306ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Uri uri = Uri.fromParts("tel", number, null);
2316ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Log.d(TAG, "android.telecom.TelecomManager#placeCall: " + uri);
2326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mTelecomManager.placeCall(uri, null);
2336ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2343d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2356ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void answerCall(UiCall uiCall) {
2366ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2376ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "answerCall: " + uiCall);
2386ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2393d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2406ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
2416ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
2426ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.answer(0);
2436ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2446ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2453d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void rejectCall(UiCall uiCall, boolean rejectWithMessage, String textMessage) {
2476ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2486ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "rejectCall: " + uiCall + ", rejectWithMessage: " + rejectWithMessage
2496ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    + "textMessage: " + textMessage);
2506ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2513d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2526ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
2536ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
2546ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.reject(rejectWithMessage, textMessage);
2556ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2566ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2573d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2586ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void disconnectCall(UiCall uiCall) {
2596ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2606ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "disconnectCall: " + uiCall);
2616ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2623d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2636ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
2646ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
2656ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.disconnect();
2666ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2676ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2683d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2696ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public List<UiCall> getCalls() {
2706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return new ArrayList<>(mCallMapping.keySet());
2716ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2723d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2736ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public boolean getMuted() {
2746ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2756ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "getMuted");
2766ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2776ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (mInCallService == null) {
2786ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            return false;
2796ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2806ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        CallAudioState audioState = mInCallService.getCallAudioState();
2816ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return audioState != null && audioState.isMuted();
2826ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2833d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2846ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void setMuted(boolean muted) {
2856ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2866ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "setMuted: " + muted);
2876ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2886ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (mInCallService == null) {
2896ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            return;
2906ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2916ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mInCallService.setMuted(muted);
2926ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
2933d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2946ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public int getSupportedAudioRouteMask() {
2956ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
2966ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "getSupportedAudioRouteMask");
2976ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
2983d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
2996ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        CallAudioState audioState = getCallAudioStateOrNull();
3006ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return audioState != null ? audioState.getSupportedRouteMask() : 0;
3016ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
3023d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
303684d175b191db416438ba00355f20d16eb79cef3Yuzhou    public List<Integer> getSupportedAudioRoute() {
304684d175b191db416438ba00355f20d16eb79cef3Yuzhou        List<Integer> audioRouteList = new ArrayList<>();
305684d175b191db416438ba00355f20d16eb79cef3Yuzhou
306684d175b191db416438ba00355f20d16eb79cef3Yuzhou        boolean isBluetoothPhoneCall = isBluetoothCall();
307684d175b191db416438ba00355f20d16eb79cef3Yuzhou        if (isBluetoothPhoneCall) {
308684d175b191db416438ba00355f20d16eb79cef3Yuzhou            // if this is bluetooth phone call, we can only select audio route between vehicle
309684d175b191db416438ba00355f20d16eb79cef3Yuzhou            // and phone.
310684d175b191db416438ba00355f20d16eb79cef3Yuzhou            // Vehicle speaker route.
311684d175b191db416438ba00355f20d16eb79cef3Yuzhou            audioRouteList.add(CallAudioState.ROUTE_BLUETOOTH);
312684d175b191db416438ba00355f20d16eb79cef3Yuzhou            // Headset route.
313684d175b191db416438ba00355f20d16eb79cef3Yuzhou            audioRouteList.add(CallAudioState.ROUTE_EARPIECE);
314684d175b191db416438ba00355f20d16eb79cef3Yuzhou        } else {
315684d175b191db416438ba00355f20d16eb79cef3Yuzhou            // Most likely we are making phone call with on board SIM card.
316684d175b191db416438ba00355f20d16eb79cef3Yuzhou            int supportedAudioRouteMask = getSupportedAudioRouteMask();
317684d175b191db416438ba00355f20d16eb79cef3Yuzhou
318684d175b191db416438ba00355f20d16eb79cef3Yuzhou            if ((supportedAudioRouteMask & CallAudioState.ROUTE_EARPIECE) != 0) {
319684d175b191db416438ba00355f20d16eb79cef3Yuzhou                audioRouteList.add(CallAudioState.ROUTE_EARPIECE);
320684d175b191db416438ba00355f20d16eb79cef3Yuzhou            } else if ((supportedAudioRouteMask & CallAudioState.ROUTE_BLUETOOTH) != 0) {
321684d175b191db416438ba00355f20d16eb79cef3Yuzhou                audioRouteList.add(CallAudioState.ROUTE_BLUETOOTH);
322684d175b191db416438ba00355f20d16eb79cef3Yuzhou            } else if ((supportedAudioRouteMask & CallAudioState.ROUTE_WIRED_HEADSET) != 0) {
323684d175b191db416438ba00355f20d16eb79cef3Yuzhou                audioRouteList.add(CallAudioState.ROUTE_WIRED_HEADSET);
324684d175b191db416438ba00355f20d16eb79cef3Yuzhou            } else if ((supportedAudioRouteMask & CallAudioState.ROUTE_SPEAKER) != 0) {
325684d175b191db416438ba00355f20d16eb79cef3Yuzhou                audioRouteList.add(CallAudioState.ROUTE_SPEAKER);
326684d175b191db416438ba00355f20d16eb79cef3Yuzhou            }
327684d175b191db416438ba00355f20d16eb79cef3Yuzhou        }
328684d175b191db416438ba00355f20d16eb79cef3Yuzhou
329684d175b191db416438ba00355f20d16eb79cef3Yuzhou        return audioRouteList;
330684d175b191db416438ba00355f20d16eb79cef3Yuzhou    }
331684d175b191db416438ba00355f20d16eb79cef3Yuzhou
332684d175b191db416438ba00355f20d16eb79cef3Yuzhou    public boolean isBluetoothCall() {
333684d175b191db416438ba00355f20d16eb79cef3Yuzhou        PhoneAccountHandle phoneAccountHandle =
334684d175b191db416438ba00355f20d16eb79cef3Yuzhou                mTelecomManager.getUserSelectedOutgoingPhoneAccount();
335684d175b191db416438ba00355f20d16eb79cef3Yuzhou        if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
336684d175b191db416438ba00355f20d16eb79cef3Yuzhou            return HFP_CLIENT_CONNECTION_SERVICE_CLASS_NAME.equals(
337684d175b191db416438ba00355f20d16eb79cef3Yuzhou                    phoneAccountHandle.getComponentName().getClassName());
338684d175b191db416438ba00355f20d16eb79cef3Yuzhou        } else {
339684d175b191db416438ba00355f20d16eb79cef3Yuzhou            return false;
340684d175b191db416438ba00355f20d16eb79cef3Yuzhou        }
341684d175b191db416438ba00355f20d16eb79cef3Yuzhou    }
342684d175b191db416438ba00355f20d16eb79cef3Yuzhou
3436ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public int getAudioRoute() {
3446ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        CallAudioState audioState = getCallAudioStateOrNull();
3456ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        int audioRoute = audioState != null ? audioState.getRoute() : 0;
3466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
3476ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "getAudioRoute " + audioRoute);
3486ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3496ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return audioRoute;
3506ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
3516ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
352684d175b191db416438ba00355f20d16eb79cef3Yuzhou    /**
353684d175b191db416438ba00355f20d16eb79cef3Yuzhou     * Re-route the audio out phone of the ongoing phone call.
354684d175b191db416438ba00355f20d16eb79cef3Yuzhou     */
355684d175b191db416438ba00355f20d16eb79cef3Yuzhou    public void setAudioRoute(@CallAudioRoute int audioRoute) {
356684d175b191db416438ba00355f20d16eb79cef3Yuzhou        if (mBluetoothHeadsetClient != null && isBluetoothCall()) {
357684d175b191db416438ba00355f20d16eb79cef3Yuzhou            for (BluetoothDevice device : mBluetoothHeadsetClient.getConnectedDevices()) {
358684d175b191db416438ba00355f20d16eb79cef3Yuzhou                List<BluetoothHeadsetClientCall> currentCalls =
359684d175b191db416438ba00355f20d16eb79cef3Yuzhou                        mBluetoothHeadsetClient.getCurrentCalls(device);
360684d175b191db416438ba00355f20d16eb79cef3Yuzhou                if (currentCalls != null && !currentCalls.isEmpty()) {
361684d175b191db416438ba00355f20d16eb79cef3Yuzhou                    if (audioRoute == CallAudioState.ROUTE_BLUETOOTH) {
362684d175b191db416438ba00355f20d16eb79cef3Yuzhou                        mBluetoothHeadsetClient.connectAudio(device);
363684d175b191db416438ba00355f20d16eb79cef3Yuzhou                    } else if ((audioRoute & CallAudioState.ROUTE_WIRED_OR_EARPIECE) != 0) {
364684d175b191db416438ba00355f20d16eb79cef3Yuzhou                        mBluetoothHeadsetClient.disconnectAudio(device);
365684d175b191db416438ba00355f20d16eb79cef3Yuzhou                    }
366684d175b191db416438ba00355f20d16eb79cef3Yuzhou                }
367684d175b191db416438ba00355f20d16eb79cef3Yuzhou            }
368684d175b191db416438ba00355f20d16eb79cef3Yuzhou        }
369684d175b191db416438ba00355f20d16eb79cef3Yuzhou        // TODO: Implement routing audio if current call is not a bluetooth call.
3706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
3716ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
3726ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void holdCall(UiCall uiCall) {
3736ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
3746ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "holdCall: " + uiCall);
3756ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3766ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
3776ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
3786ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
3796ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.hold();
3806ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3816ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
3823d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
3836ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void unholdCall(UiCall uiCall) {
3846ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
3856ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "unholdCall: " + uiCall);
3866ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3876ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
3886ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
3896ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
3906ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.unhold();
3916ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3926ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
3936ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
3946ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void playDtmfTone(UiCall uiCall, char digit) {
3956ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
3966ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "playDtmfTone: call: " + uiCall + ", digit: " + digit);
3976ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
3986ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
3996ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
4006ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
4016ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.playDtmfTone(digit);
4026ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4036ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4046ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4056ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void stopDtmfTone(UiCall uiCall) {
4066ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
4076ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "stopDtmfTone: call: " + uiCall);
4086ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4096ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4106ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
4116ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
4126ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.stopDtmfTone();
4136ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4146ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4156ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4166ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void postDialContinue(UiCall uiCall, boolean proceed) {
4176ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
4186ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "postDialContinue: call: " + uiCall + ", proceed: " + proceed);
4196ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4206ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4216ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
4226ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
4236ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.postDialContinue(proceed);
4246ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4256ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4266ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4276ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void conference(UiCall uiCall, UiCall otherUiCall) {
4286ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
4296ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "conference: call: " + uiCall + ", otherCall: " + otherUiCall);
4306ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4316ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
4336ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call otherTelecomCall = mCallMapping.get(otherUiCall);
4346ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
4356ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.conference(otherTelecomCall);
4366ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4376ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4386ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4396ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    public void splitFromConference(UiCall uiCall) {
4406ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
4416ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "splitFromConference: call: " + uiCall);
4426ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4436ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4446ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call telecomCall = mCallMapping.get(uiCall);
4456ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall != null) {
4466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.splitFromConference();
4476ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4486ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4496ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4506ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private UiCall doTelecomCallAdded(final Call telecomCall) {
4516ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Log.d(TAG, "doTelecomCallAdded: " + telecomCall);
4526ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4536ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        UiCall uiCall = getOrCreateCallContainer(telecomCall);
4546ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        telecomCall.registerCallback(new TelecomCallListener(this, uiCall));
4556ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (CallListener listener : mCallListeners) {
4566ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            listener.onCallAdded(uiCall);
4576ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4586ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Log.d(TAG, "Call backs registered");
4596ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4606ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (telecomCall.getState() == Call.STATE_SELECT_PHONE_ACCOUNT) {
4616ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            // TODO(b/26189994): need to show Phone Account picker to let user choose a phone
4626ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            // account. It should be an account from TelecomManager#getCallCapablePhoneAccounts
4636ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            // list.
4646ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.w(TAG, "Need to select phone account for the given call: " + telecomCall + ", "
4656ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    + "but this feature is not implemented yet.");
4666ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            telecomCall.disconnect();
4676ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4686ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return uiCall;
4696ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4716ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private void doTelecomCallRemoved(Call telecomCall) {
4726ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        UiCall uiCall = getOrCreateCallContainer(telecomCall);
4736ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4746ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mCallMapping.remove(uiCall);
4756ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4766ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (CallListener listener : mCallListeners) {
4776ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            listener.onCallRemoved(uiCall);
4786ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4796ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4806ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4816ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private void doCallAudioStateChanged(CallAudioState audioState) {
4826ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (CallListener listener : mCallListeners) {
4836ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            listener.onAudioStateChanged(audioState.isMuted(), audioState.getRoute(),
4846ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    audioState.getSupportedRouteMask());
4856ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4866ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4876ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4886ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private void onStateChanged(UiCall uiCall, int state) {
4896ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (CallListener listener : mCallListeners) {
490bb51ff67f2973488079684ca58924a1c4b712dabJohnny Zhou            listener.onCallStateChanged(uiCall, state);
4916ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4926ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4936ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
4946ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private void onCallUpdated(UiCall uiCall) {
4956ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (CallListener listener : mCallListeners) {
4966ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            listener.onCallUpdated(uiCall);
4976ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
4986ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
4996ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5006ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private UiCall getOrCreateCallContainer(Call telecomCall) {
5016ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        for (Map.Entry<UiCall, Call> entry : mCallMapping.entrySet()) {
5026ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (entry.getValue() == telecomCall) {
5036ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                return entry.getKey();
5046ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
5056ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
5066ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5076ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        UiCall uiCall = new UiCall(nextCarPhoneCallId++);
5086ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        updateCallContainerFromTelecom(uiCall, telecomCall);
5096ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        mCallMapping.put(uiCall, telecomCall);
5106ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return uiCall;
5116ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
5126ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5136ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private static void updateCallContainerFromTelecom(UiCall uiCall, Call telecomCall) {
5146ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (Log.isLoggable(TAG, Log.DEBUG)) {
5156ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            Log.d(TAG, "updateCallContainerFromTelecom: call: " + uiCall + ", telecomCall: "
5166ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                    + telecomCall);
5176ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
5186ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5196ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setState(telecomCall.getState());
5206ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setHasChildren(!telecomCall.getChildren().isEmpty());
5216ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setHasParent(telecomCall.getParent() != null);
5226ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5236ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        Call.Details details = telecomCall.getDetails();
5246ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (details == null) {
5256ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            return;
5266ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
5276ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5286ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setConnectTimeMillis(details.getConnectTimeMillis());
5296ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5306ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        DisconnectCause cause = details.getDisconnectCause();
5316ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setDisconnectCause(cause == null ? null : cause.getLabel());
5326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5336ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        GatewayInfo gatewayInfo = details.getGatewayInfo();
5346ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setGatewayInfoOriginalAddress(
5356ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                gatewayInfo == null ? null : gatewayInfo.getOriginalAddress());
5366ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5376ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        String number = "";
5386ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        if (gatewayInfo != null) {
5396ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart();
5406ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        } else if (details.getHandle() != null) {
5416ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            number = details.getHandle().getSchemeSpecificPart();
5426ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
5436ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        uiCall.setNumber(number);
5446ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
5456ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
5466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private CallAudioState getCallAudioStateOrNull() {
5476ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        return mInCallService != null ? mInCallService.getCallAudioState() : null;
5486ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
5493d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5503d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    /** Returns a first call that matches at least one provided call state */
5513d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public UiCall getCallWithState(int... callStates) {
5523d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
5533d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "getCallWithState: " + callStates);
5543d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5553d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        for (UiCall call : getCalls()) {
5563d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            for (int callState : callStates) {
5573d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                if (call.getState() == callState) {
5583d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                    return call;
5593d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                }
5603d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            }
5613d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5623d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return null;
5633d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
5643d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5653d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public UiCall getPrimaryCall() {
5663d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
5673d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "getPrimaryCall");
5683d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5693d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        List<UiCall> calls = getCalls();
5703d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (calls.isEmpty()) {
5713d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return null;
5723d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5733d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5743d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        Collections.sort(calls, getCallComparator());
5753d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        UiCall uiCall = calls.get(0);
5763d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (uiCall.hasParent()) {
5773d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return null;
5783d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5793d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return uiCall;
5803d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
5813d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5823d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public UiCall getSecondaryCall() {
5833d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
5843d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "getSecondaryCall");
5853d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5863d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        List<UiCall> calls = getCalls();
5873d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (calls.size() < 2) {
5883d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return null;
5893d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5903d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5913d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        Collections.sort(calls, getCallComparator());
5923d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        UiCall uiCall = calls.get(1);
5933d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (uiCall.hasParent()) {
5943d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return null;
5953d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
5963d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return uiCall;
5973d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
5983d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
5993d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public static final int CAN_PLACE_CALL_RESULT_OK = 0;
6003d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public static final int CAN_PLACE_CALL_RESULT_NETWORK_UNAVAILABLE = 1;
6013d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public static final int CAN_PLACE_CALL_RESULT_HFP_UNAVAILABLE = 2;
6023d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public static final int CAN_PLACE_CALL_RESULT_AIRPLANE_MODE = 3;
6033d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6043d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public int getCanPlaceCallStatus(String number, boolean bluetoothRequired) {
6053d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        // TODO(b/26191392): figure out the logic for projected and embedded modes
6063d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return CAN_PLACE_CALL_RESULT_OK;
6073d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
6083d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6093d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public String getFailToPlaceCallMessage(int canPlaceCallResult) {
6103d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        switch (canPlaceCallResult) {
6113d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            case CAN_PLACE_CALL_RESULT_OK:
6123d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                return "";
6133d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            case CAN_PLACE_CALL_RESULT_HFP_UNAVAILABLE:
6143d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                return mContext.getString(R.string.error_no_hfp);
6153d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            case CAN_PLACE_CALL_RESULT_AIRPLANE_MODE:
6163d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                return mContext.getString(R.string.error_airplane_mode);
6173d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            case CAN_PLACE_CALL_RESULT_NETWORK_UNAVAILABLE:
6183d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            default:
6193d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                return mContext.getString(R.string.error_network_not_available);
6203d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6213d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
6223d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6233d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    /** Places call only if there's no outgoing call right now */
6243d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public void safePlaceCall(String number, boolean bluetoothRequired) {
6253d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
6263d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "safePlaceCall: " + number);
6273d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6283d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6293d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        int placeCallStatus = getCanPlaceCallStatus(number, bluetoothRequired);
6303d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (placeCallStatus != CAN_PLACE_CALL_RESULT_OK) {
6313d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            if (Log.isLoggable(TAG, Log.DEBUG)) {
6323d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                Log.d(TAG, "Unable to place a call: " + placeCallStatus);
6333d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            }
6343d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return;
6353d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6363d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6373d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        UiCall outgoingCall = getCallWithState(
6383d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                Call.STATE_CONNECTING, Call.STATE_NEW, Call.STATE_DIALING);
6393d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (outgoingCall == null) {
6403d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            long now = Calendar.getInstance().getTimeInMillis();
6413d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            if (now - mLastPlacedCallTimeMs > MIN_TIME_BETWEEN_CALLS_MS) {
6423d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                placeCall(number);
6433d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                mLastPlacedCallTimeMs = now;
6443d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            } else {
6453d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                if (Log.isLoggable(TAG, Log.INFO)) {
6463d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                    Log.i(TAG, "You have to wait " + MIN_TIME_BETWEEN_CALLS_MS
6473d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                            + "ms between making calls");
6483d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                }
6493d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            }
6503d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6513d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
6523d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6533d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public void callVoicemail() {
6543d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
6553d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "callVoicemail");
6563d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6573d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6583d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        String voicemailNumber = TelecomUtils.getVoicemailNumber(mContext);
6593d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (TextUtils.isEmpty(voicemailNumber)) {
6603d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.w(TAG, "Unable to get voicemail number.");
6613d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            return;
6623d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6633d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        safePlaceCall(voicemailNumber, false);
6643d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
6653d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6663d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    /**
6673d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     * Returns the call types for the given number of items in the cursor.
6683d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     * <p/>
6693d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     * It uses the next {@code count} rows in the cursor to extract the types.
6703d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     * <p/>
6713d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     * Its position in the cursor is unchanged by this function.
6723d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer     */
6733d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    public int[] getCallTypes(Cursor cursor, int count) {
6743d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        if (Log.isLoggable(TAG, Log.DEBUG)) {
6753d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            Log.d(TAG, "getCallTypes: cursor: " + cursor + ", count: " + count);
6763d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6773d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6783d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        int position = cursor.getPosition();
6793d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        int[] callTypes = new int[count];
6803d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        String voicemailNumber = mTelephonyManager.getVoiceMailNumber();
6813d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        int column;
6823d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        for (int index = 0; index < count; ++index) {
6833d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            column = cursor.getColumnIndex(CallLog.Calls.NUMBER);
6843d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            String phoneNumber = cursor.getString(column);
6853d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            if (phoneNumber != null && phoneNumber.equals(voicemailNumber)) {
6863d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                callTypes[index] = PhoneLoader.VOICEMAIL_TYPE;
6873d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            } else {
6883d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                column = cursor.getColumnIndex(CallLog.Calls.TYPE);
6893d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                callTypes[index] = cursor.getInt(column);
6903d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            }
6913d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            cursor.moveToNext();
6923d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        }
6933d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        cursor.moveToPosition(position);
6943d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return callTypes;
6953d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
6963d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
6973d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    private static Comparator<UiCall> getCallComparator() {
6983d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        return new Comparator<UiCall>() {
6993d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            @Override
7003d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            public int compare(UiCall call, UiCall otherCall) {
7013d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                if (call.hasParent() && !otherCall.hasParent()) {
7023d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                    return 1;
7033d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                } else if (!call.hasParent() && otherCall.hasParent()) {
7043d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                    return -1;
7053d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                }
7063d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                int carCallRank = sCallStateRank.indexOf(call.getState());
7073d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                int otherCarCallRank = sCallStateRank.indexOf(otherCall.getState());
7083d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer
7093d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer                return otherCarCallRank - carCallRank;
7103d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer            }
7113d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer        };
7123d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer    }
7136ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7146ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    private static class TelecomCallListener extends Call.Callback {
7156ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        private final WeakReference<UiCallManager> mCarTelecomMangerRef;
7166ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        private final WeakReference<UiCall> mCallContainerRef;
7176ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7186ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        TelecomCallListener(UiCallManager carTelecomManager, UiCall uiCall) {
7196ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mCarTelecomMangerRef = new WeakReference<>(carTelecomManager);
7206ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            mCallContainerRef = new WeakReference<>(uiCall);
7216ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7226ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7236ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7246ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onStateChanged(Call telecomCall, int state) {
7256ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (Log.isLoggable(TAG, Log.DEBUG)) {
7266ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                Log.d(TAG, "onStateChanged: " + state);
7276ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
7286ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            UiCallManager manager = mCarTelecomMangerRef.get();
7296ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            UiCall call = mCallContainerRef.get();
7306ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (manager != null && call != null) {
7316ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                call.setState(state);
7326ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                manager.onStateChanged(call, state);
7336ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
7346ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7356ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7366ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7376ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onParentChanged(Call telecomCall, Call parent) {
7386ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            doCallUpdated(telecomCall);
7396ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7406ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7416ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7426ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onCallDestroyed(Call telecomCall) {
7436ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (Log.isLoggable(TAG, Log.DEBUG)) {
7446ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                Log.d(TAG, "onCallDestroyed");
7456ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
7466ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7476ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7486ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7496ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onDetailsChanged(Call telecomCall, Call.Details details) {
7506ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            doCallUpdated(telecomCall);
7516ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7526ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7536ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7546ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onVideoCallChanged(Call telecomCall, InCallService.VideoCall videoCall) {
7556ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            doCallUpdated(telecomCall);
7566ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7576ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7586ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7596ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onCannedTextResponsesLoaded(Call telecomCall,
7606ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                List<String> cannedTextResponses) {
7616ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            doCallUpdated(telecomCall);
7626ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7636ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7646ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        @Override
7656ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        public void onChildrenChanged(Call telecomCall, List<Call> children) {
7666ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            doCallUpdated(telecomCall);
7676ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7686ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan
7696ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        private void doCallUpdated(Call telecomCall) {
7706ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            UiCallManager manager = mCarTelecomMangerRef.get();
7716ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            UiCall uiCall = mCallContainerRef.get();
7726ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            if (manager != null && uiCall != null) {
7736ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                updateCallContainerFromTelecom(uiCall, telecomCall);
7746ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan                manager.onCallUpdated(uiCall);
7756ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan            }
7766ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan        }
7776ed89513a64aeaadc55268607e556c8299d4d6b4Srinivas Visvanathan    }
7783d724a4b70dd9a08d9adddc3b403edfd572bcaa6Rakesh Iyer}
779