1e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon/*
2e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * Copyright (C) 2014 The Android Open Source Project
3e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon *
4e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
5e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * you may not use this file except in compliance with the License.
6e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * You may obtain a copy of the License at
7e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon *
8e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
9e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon *
10e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * Unless required by applicable law or agreed to in writing, software
11e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
12e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * See the License for the specific language governing permissions and
14e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * limitations under the License.
15e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon */
16e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
18e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
1961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport android.Manifest;
20e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.content.ComponentName;
21e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.content.Context;
22e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.content.Intent;
23e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.content.ServiceConnection;
2461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport android.content.pm.PackageManager;
2561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport android.content.pm.ResolveInfo;
2661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport android.content.pm.ServiceInfo;
27fdfcafaa7913adab35724c9fc7560fbbbb1561aeSantos Cordonimport android.content.res.Resources;
2884fa5f8c59a1cd3c5716c239fb19c58244fc81feSailesh Nepalimport android.net.Uri;
29e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.os.IBinder;
30e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordonimport android.os.RemoteException;
3160e7584fe6a1ab878458e4e89f83b77f30416f46Amith Yamasaniimport android.os.UserHandle;
327cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.AudioState;
337cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.CallProperties;
347cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.CallState;
357cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.InCallService;
367cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.ParcelableCall;
377cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.PhoneCapabilities;
387cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.TelecomManager;
3961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport android.util.ArrayMap;
40a439e1b6d6201dedecfc40b67c9347a8c563b9c1Sailesh Nepal
4191d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn// TODO: Needed for move to system service: import com.android.internal.R;
427cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport com.android.internal.telecom.IInCallService;
43810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepalimport com.google.common.collect.ImmutableCollection;
44e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
45a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordonimport java.util.ArrayList;
4661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport java.util.Iterator;
47a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordonimport java.util.List;
4861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport java.util.Map;
4961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunnimport java.util.concurrent.ConcurrentHashMap;
50a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon
51e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon/**
52e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
53e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon * can send updates to the in-call app. This class is created and owned by CallsManager and retains
54e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal * a binding to the {@link IInCallService} (implemented by the in-call app).
55e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon */
56810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepalpublic final class InCallController extends CallsManagerListenerBase {
57e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    /**
58e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * Used to bind to the in-call app and triggers the start of communication between
59e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal     * this class and in-call app.
60e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     */
61e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    private class InCallServiceConnection implements ServiceConnection {
62e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        /** {@inheritDoc} */
63e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        @Override public void onServiceConnected(ComponentName name, IBinder service) {
64aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee            Log.d(this, "onServiceConnected: %s", name);
6561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            onConnected(name, service);
66e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        }
67e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
68e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        /** {@inheritDoc} */
69e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        @Override public void onServiceDisconnected(ComponentName name) {
70aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee            Log.d(this, "onDisconnected: %s", name);
7161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            onDisconnected(name);
72e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        }
73e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    }
74e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
75e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal    private final Call.Listener mCallListener = new Call.ListenerBase() {
76e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
77e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        public void onCallCapabilitiesChanged(Call call) {
78e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
79e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
80e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
81e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
82e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        public void onCannedSmsResponsesLoaded(Call call) {
83e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
84e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
85e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
86e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
873bcf935b25470e94c8c6855862434d15b785c54aAndrew Lee        public void onVideoCallProviderChanged(Call call) {
88e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
89e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
90e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
91e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
92e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        public void onStatusHintsChanged(Call call) {
93e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
94e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
95e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
96e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
97e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        public void onHandleChanged(Call call) {
98e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
99e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
100e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
101e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        @Override
102e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        public void onCallerDisplayNameChanged(Call call) {
103e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal            updateCall(call);
104e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        }
1054a79660c984d54679ad4d2bdc89e224d9c8c375bAndrew Lee
1064a79660c984d54679ad4d2bdc89e224d9c8c375bAndrew Lee        @Override
1074a79660c984d54679ad4d2bdc89e224d9c8c375bAndrew Lee        public void onVideoStateChanged(Call call) {
1084a79660c984d54679ad4d2bdc89e224d9c8c375bAndrew Lee            updateCall(call);
1094a79660c984d54679ad4d2bdc89e224d9c8c375bAndrew Lee        }
1109d58de5df05d0358fc8ae57ab7e9a0c45337c602Sailesh Nepal
1119d58de5df05d0358fc8ae57ab7e9a0c45337c602Sailesh Nepal        @Override
112b78b27693afbe9736f0a54ec473328955251f885Ihab Awad        public void onTargetPhoneAccountChanged(Call call) {
11369eb0f582babcedc1dc5e6613a27867be6e8d0e0Ihab Awad            updateCall(call);
11469eb0f582babcedc1dc5e6613a27867be6e8d0e0Ihab Awad        }
11512d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon
11612d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        @Override
11712d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        public void onConferenceableCallsChanged(Call call) {
11812d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon            updateCall(call);
11912d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        }
120e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal    };
121e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
12261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    /**
12361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * Maintains a binding connection to the in-call app(s).
12461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
12561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * load factor before resizing, 1 means we only expect a single thread to
12661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * access the map so make only a single shard
12761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     */
12861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    private final Map<ComponentName, InCallServiceConnection> mServiceConnections =
12961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            new ConcurrentHashMap<ComponentName, InCallServiceConnection>(8, 0.9f, 1);
130e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
13161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    /** The in-call app implementations, see {@link IInCallService}. */
13261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    private final Map<ComponentName, IInCallService> mInCallServices = new ArrayMap<>();
133e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
134e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal    private final CallIdMapper mCallIdMapper = new CallIdMapper("InCall");
135e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal
13612bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn    /** The {@link ComponentName} of the default InCall UI. */
137d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon    private final ComponentName mInCallComponentName;
13861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
13991d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    private final Context mContext;
14091d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn
14191d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn    public InCallController(Context context) {
14291d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        mContext = context;
14391d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn        Resources resources = mContext.getResources();
14461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
14561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        mInCallComponentName = new ComponentName(
14661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                resources.getString(R.string.ui_default_package),
14761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                resources.getString(R.string.incall_default_class));
148e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    }
149e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
150810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    @Override
151810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    public void onCallAdded(Call call) {
15261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (mInCallServices.isEmpty()) {
153810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            bind();
154810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal        } else {
155aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee            Log.i(this, "onCallAdded: %s", call);
15661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            // Track the call if we don't already know about it.
15761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            addCall(call);
15861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
15912bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn            for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
16012bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                ComponentName componentName = entry.getKey();
16112bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                IInCallService inCallService = entry.getValue();
16212bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn
16312bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                ParcelableCall parcelableCall = toParcelableCall(call,
16412bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                        componentName.equals(mInCallComponentName) /* includeVideoProvider */);
165ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad                try {
16661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    inCallService.addCall(parcelableCall);
167ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad                } catch (RemoteException ignored) {
168ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad                }
169049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon            }
170049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon        }
171049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    }
172049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon
173810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    @Override
174810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    public void onCallRemoved(Call call) {
175aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee        Log.i(this, "onCallRemoved: %s", call);
176810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal        if (CallsManager.getInstance().getCalls().isEmpty()) {
177df39986de36d40aaa45e61d19a21eca536765ca5Santos Cordon            // TODO: Wait for all messages to be delivered to the service before unbinding.
178810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            unbind();
179049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon        }
180e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        call.removeListener(mCallListener);
181e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal        mCallIdMapper.removeCall(call);
182049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    }
183049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon
184810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    @Override
1856fb37c87836b5245046bd3b14320823ab839a10cIhab Awad    public void onCallStateChanged(Call call, int oldState, int newState) {
1868c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        updateCall(call);
187cdf3ebd3ea6505668304b7e0a39df354ebbb52fbYorke Lee    }
188cdf3ebd3ea6505668304b7e0a39df354ebbb52fbYorke Lee
1896aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    @Override
190c92c436d84de46bb85100df9138378d9ffe0f2f2Sailesh Nepal    public void onConnectionServiceChanged(
1918c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal            Call call,
192c92c436d84de46bb85100df9138378d9ffe0f2f2Sailesh Nepal            ConnectionServiceWrapper oldService,
193c92c436d84de46bb85100df9138378d9ffe0f2f2Sailesh Nepal            ConnectionServiceWrapper newService) {
1948c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        updateCall(call);
1958c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal    }
1968c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal
1978c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal    @Override
1986fb37c87836b5245046bd3b14320823ab839a10cIhab Awad    public void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) {
19961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (!mInCallServices.isEmpty()) {
2006aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldAudioState,
2016aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal                    newAudioState);
20261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            for (IInCallService inCallService : mInCallServices.values()) {
20361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                try {
20461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    inCallService.onAudioStateChanged(newAudioState);
20561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                } catch (RemoteException ignored) {
20661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
2076aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            }
2086aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        }
2096aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    }
2106aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
211352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton    void onPostDialWait(Call call, String remaining) {
21261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (!mInCallServices.isEmpty()) {
213352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton            Log.i(this, "Calling onPostDialWait, remaining = %s", remaining);
21461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            for (IInCallService inCallService : mInCallServices.values()) {
21561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                try {
21661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    inCallService.setPostDialWait(mCallIdMapper.getCallId(call), remaining);
21761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                } catch (RemoteException ignored) {
21861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
219352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton            }
220352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton        }
221352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton    }
222352105c5d33ac94e5ad0cb5ac2e9268731423e65Evan Charlton
223a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon    @Override
224a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon    public void onIsConferencedChanged(Call call) {
2250fbe6321e341ddce186634266dba2db3fa426b88Santos Cordon        Log.d(this, "onIsConferencedChanged %s", call);
226a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        updateCall(call);
227a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon    }
228a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon
229f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon    void bringToForeground(boolean showDialpad) {
23061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (!mInCallServices.isEmpty()) {
23161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            for (IInCallService inCallService : mInCallServices.values()) {
23261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                try {
23361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    inCallService.bringToForeground(showDialpad);
23461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                } catch (RemoteException ignored) {
23561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
236f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon            }
237f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon        } else {
238f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon            Log.w(this, "Asking to bring unbound in-call UI to foreground.");
239f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon        }
240f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon    }
241f3671a6f38a4af68603daf4c2025f572129eada8Santos Cordon
242049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    /**
243049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon     * Unbinds an existing bound connection to the in-call app.
244049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon     */
245810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal    private void unbind() {
246049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon        ThreadUtil.checkOnMainThread();
247d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon        Iterator<Map.Entry<ComponentName, InCallServiceConnection>> iterator =
248d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            mServiceConnections.entrySet().iterator();
249d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon        while (iterator.hasNext()) {
250d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            Log.i(this, "Unbinding from InCallService %s");
25191d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn            mContext.unbindService(iterator.next().getValue());
252d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            iterator.remove();
253049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon        }
254d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon        mInCallServices.clear();
255049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    }
256049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon
257049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    /**
258e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * Binds to the in-call app if not already connected by binding directly to the saved
259e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * component name of the {@link IInCallService} implementation.
260e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     */
261049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon    private void bind() {
262e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        ThreadUtil.checkOnMainThread();
26361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (mInCallServices.isEmpty()) {
26491d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn            PackageManager packageManager = mContext.getPackageManager();
265d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
26661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
267d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {
26861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                ServiceInfo serviceInfo = entry.serviceInfo;
26961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                if (serviceInfo != null) {
27061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    boolean hasServiceBindPermission = serviceInfo.permission != null &&
27161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                            serviceInfo.permission.equals(
27261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                                    Manifest.permission.BIND_INCALL_SERVICE);
27361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    boolean hasControlInCallPermission = packageManager.checkPermission(
27461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                            Manifest.permission.CONTROL_INCALL_EXPERIENCE,
27561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                            serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED;
27661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
27761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    if (!hasServiceBindPermission) {
27861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                        Log.w(this, "InCallService does not have BIND_INCALL_SERVICE permission: " +
27961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                                serviceInfo.packageName);
28061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                        continue;
28161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    }
28261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
28361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    if (!hasControlInCallPermission) {
28461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                        Log.w(this,
28561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                                "InCall UI does not have CONTROL_INCALL_EXPERIENCE permission: " +
28661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                                        serviceInfo.packageName);
28761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                        continue;
28861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    }
28961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
29061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();
29161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    ComponentName componentName = new ComponentName(serviceInfo.packageName,
29261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                            serviceInfo.name);
29361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
2944f50f8e29d7479351ec344ec592686708ff01055Santos Cordon                    Log.i(this, "Attempting to bind to InCall %s, is dupe? %b ",
2954f50f8e29d7479351ec344ec592686708ff01055Santos Cordon                            serviceInfo.packageName,
2964f50f8e29d7479351ec344ec592686708ff01055Santos Cordon                            mServiceConnections.containsKey(componentName));
2974f50f8e29d7479351ec344ec592686708ff01055Santos Cordon
298d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                    if (!mServiceConnections.containsKey(componentName)) {
299d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                        Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
300d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                        intent.setComponent(componentName);
301d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon
30291d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn                        if (mContext.bindServiceAsUser(intent, inCallServiceConnection,
303d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                                Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
304d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                            mServiceConnections.put(componentName, inCallServiceConnection);
305d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                        }
30661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    }
30761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
308e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon            }
309e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        }
310e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    }
311e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
312e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    /**
313e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * Persists the {@link IInCallService} instance and starts the communication between
314e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal     * this class and in-call app by sending the first update to in-call app. This method is
315e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * called after a successful binding connection is established.
316e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     *
31761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * @param componentName The service {@link ComponentName}.
318e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     * @param service The {@link IInCallService} implementation.
319e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     */
32061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    private void onConnected(ComponentName componentName, IBinder service) {
321e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        ThreadUtil.checkOnMainThread();
32261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
323aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee        Log.i(this, "onConnected to %s", componentName);
324aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee
32561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        IInCallService inCallService = IInCallService.Stub.asInterface(service);
326e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
327e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        try {
32861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),
329e59bb195972d65a71618af4fe13f1ad982253e16Sailesh Nepal                    mCallIdMapper));
33061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            mInCallServices.put(componentName, inCallService);
331e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        } catch (RemoteException e) {
332f1c191d3974fed3f57680c63571ae0212c4622e7Sailesh Nepal            Log.e(this, e, "Failed to set the in-call adapter.");
333810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            return;
334e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        }
335e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
33661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        // Upon successful connection, send the state of the world to the service.
337810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal        ImmutableCollection<Call> calls = CallsManager.getInstance().getCalls();
338810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal        if (!calls.isEmpty()) {
339aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee            Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(),
340aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee                    componentName);
341810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            for (Call call : calls) {
34261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                try {
34361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    // Track the call if we don't already know about it.
344aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee                    Log.i(this, "addCall after binding: %s", call);
34561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    addCall(call);
34661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
34712bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                    inCallService.addCall(toParcelableCall(call,
34812bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                            componentName.equals(mInCallComponentName) /* includeVideoProvider */));
34961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                } catch (RemoteException ignored) {
35061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
351810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            }
3526aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            onAudioStateChanged(null, CallsManager.getInstance().getAudioState());
353810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal        } else {
354810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal            unbind();
355049b7b6a8d0cf44d687d827cb57a18fa23358206Santos Cordon        }
356e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    }
357e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon
358e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    /**
35961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * Cleans up an instance of in-call app after the service has been unbound.
36061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     *
36161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * @param disconnectedComponent The {@link ComponentName} of the service which disconnected.
362e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon     */
36361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    private void onDisconnected(ComponentName disconnectedComponent) {
364aaa88dd2d46639092b95b290ac28ac81e8319dd3Yorke Lee        Log.i(this, "onDisconnected from %s", disconnectedComponent);
365e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon        ThreadUtil.checkOnMainThread();
366d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon
36761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (mInCallServices.containsKey(disconnectedComponent)) {
36861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            mInCallServices.remove(disconnectedComponent);
36961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        }
37061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
371d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon        if (mServiceConnections.containsKey(disconnectedComponent)) {
372d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            // One of the services that we were bound to has disconnected. If the default in-call UI
373d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            // has disconnected, disconnect all calls and un-bind all other InCallService
374d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            // implementations.
375d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            if (disconnectedComponent.equals(mInCallComponentName)) {
376d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                Log.i(this, "In-call UI %s disconnected.", disconnectedComponent);
377d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                CallsManager.getInstance().disconnectAllCalls();
378d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                unbind();
379d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon            } else {
380d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                Log.i(this, "In-Call Service %s suddenly disconnected", disconnectedComponent);
381d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                // Else, if it wasn't the default in-call UI, then one of the other in-call services
382d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                // disconnected and, well, that's probably their fault.  Clear their state and
383d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                // ignore.
384d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                InCallServiceConnection serviceConnection =
385d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                        mServiceConnections.get(disconnectedComponent);
386d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon
387d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                // We still need to call unbind even though it disconnected.
38891d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn                mContext.unbindService(serviceConnection);
389d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon
390d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                mServiceConnections.remove(disconnectedComponent);
391d93a441281e2505cc305b8037847feb1a27f9c08Santos Cordon                mInCallServices.remove(disconnectedComponent);
39261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            }
39361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        }
394e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon    }
3958c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal
39612bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn    /**
39712bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * Informs all {@link InCallService} instances of the updated call information.  Changes to the
39812bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * video provider are only communicated to the default in-call UI.
39912bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     *
40012bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * @param call The {@link Call}.
40112bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     */
4028c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal    private void updateCall(Call call) {
40361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (!mInCallServices.isEmpty()) {
40412bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn            for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
40512bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                ComponentName componentName = entry.getKey();
40612bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                IInCallService inCallService = entry.getValue();
40712bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                ParcelableCall parcelableCall = toParcelableCall(call,
40812bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                        componentName.equals(mInCallComponentName) /* includeVideoProvider */);
40912bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn
41012bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                Log.v(this, "updateCall %s ==> %s", call, parcelableCall);
41161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                try {
41261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                    inCallService.updateCall(parcelableCall);
41361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                } catch (RemoteException ignored) {
41461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn                }
4158c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal            }
4168c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        }
4178c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal    }
4188c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal
41912bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn    /**
42012bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * Parcels all information for a {@link Call} into a new {@link ParcelableCall} instance.
42112bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     *
42212bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * @param call The {@link Call} to parcel.
42312bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * @param includeVideoProvider When {@code true}, the {@link IVideoProvider} is included in the
42412bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     *      parcelled call.  When {@code false}, the {@link IVideoProvider} is not included.
42512bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
42612bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn     */
42712bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn    private ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider) {
4288c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        String callId = mCallIdMapper.getCallId(call);
4298c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal
430e20bc974ffc24d852d09e153a280b51e0329299cSailesh Nepal        int capabilities = call.getCallCapabilities();
4314b034a6b5901291fe06db397d394c484e68d7781Santos Cordon        if (CallsManager.getInstance().isAddCallCapable(call)) {
4326fb37c87836b5245046bd3b14320823ab839a10cIhab Awad            capabilities |= PhoneCapabilities.ADD_CALL;
43310838c23aa209b364812cf1dcc23a3793234fb0dSantos Cordon        }
4341711aec69264a36fda29c825bc6fb8fbceeb1efdTyler Gunn
43510ce856c075a06e7300dfc279a88279b0716d43cYorke Lee        // Disable mute and add call for emergency calls.
4361711aec69264a36fda29c825bc6fb8fbceeb1efdTyler Gunn        if (call.isEmergencyCall()) {
4371711aec69264a36fda29c825bc6fb8fbceeb1efdTyler Gunn            capabilities &= ~PhoneCapabilities.MUTE;
43810ce856c075a06e7300dfc279a88279b0716d43cYorke Lee            capabilities &= ~PhoneCapabilities.ADD_CALL;
439a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        }
440e20bc974ffc24d852d09e153a280b51e0329299cSailesh Nepal
441bfffd2d175cc0219e512f64ff47d1e9ba5a50affAndrew Lee        int properties = call.isConference() ? CallProperties.CONFERENCE : 0;
442bfffd2d175cc0219e512f64ff47d1e9ba5a50affAndrew Lee
4436fb37c87836b5245046bd3b14320823ab839a10cIhab Awad        int state = call.getState();
4448c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        if (state == CallState.ABORTED) {
4458c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal            state = CallState.DISCONNECTED;
4465dfd3800f15e9b0f1420813d8d7638e1cf9d621dSantos Cordon        }
4475dfd3800f15e9b0f1420813d8d7638e1cf9d621dSantos Cordon
4485dfd3800f15e9b0f1420813d8d7638e1cf9d621dSantos Cordon        if (call.isLocallyDisconnecting() && state != CallState.DISCONNECTED) {
449aa47937652d80d8bd19860ec521d28ffea745e63Tyler Gunn            state = CallState.DISCONNECTING;
4508c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal        }
451a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon
452a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        String parentCallId = null;
453a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        Call parentCall = call.getParentCall();
454a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        if (parentCall != null) {
455a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon            parentCallId = mCallIdMapper.getCallId(parentCall);
456a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        }
457a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon
458a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        long connectTimeMillis = call.getConnectTimeMillis();
459a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        List<Call> childCalls = call.getChildCalls();
460a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        List<String> childCallIds = new ArrayList<>();
461a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        if (!childCalls.isEmpty()) {
462a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon            connectTimeMillis = Long.MAX_VALUE;
463a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon            for (Call child : childCalls) {
464a9173085c5c176d3f632e50e962701bba2473b2aYorke Lee                if (child.getConnectTimeMillis() > 0) {
465a9173085c5c176d3f632e50e962701bba2473b2aYorke Lee                    connectTimeMillis = Math.min(child.getConnectTimeMillis(), connectTimeMillis);
466a9173085c5c176d3f632e50e962701bba2473b2aYorke Lee                }
467a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon                childCallIds.add(mCallIdMapper.getCallId(child));
468a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon            }
469a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon        }
470a161070ea054f91a5b2d5b4e3413381134d548b8Santos Cordon
471ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad        if (call.isRespondViaSmsCapable()) {
4726fb37c87836b5245046bd3b14320823ab839a10cIhab Awad            capabilities |= PhoneCapabilities.RESPOND_VIA_TEXT;
473ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad        }
474ff7493a8f620509d41dd8a5106c1d0dcd27cd274Ihab Awad
4757cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunn        Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
476e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal                call.getHandle() : null;
477e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal        String callerDisplayName = call.getCallerDisplayNamePresentation() ==
4787cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunn                TelecomManager.PRESENTATION_ALLOWED ?  call.getCallerDisplayName() : null;
479e8ecb98d5341395e073d02c065143ae3ac76ef71Sailesh Nepal
48012d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        List<Call> conferenceableCalls = call.getConferenceableCalls();
48112d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        List<String> conferenceableCallIds = new ArrayList<String>(conferenceableCalls.size());
48212d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        for (Call otherCall : conferenceableCalls) {
48312d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon            String otherId = mCallIdMapper.getCallId(otherCall);
48412d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon            if (otherId != null) {
48512d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon                conferenceableCallIds.add(otherId);
48612d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon            }
48712d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon        }
48812d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon
4892583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon        return new ParcelableCall(
4902583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                callId,
4912583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                state,
4922583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getDisconnectCause(),
4932583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getCannedSmsResponses(),
4942583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                capabilities,
495bfffd2d175cc0219e512f64ff47d1e9ba5a50affAndrew Lee                properties,
4962583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                connectTimeMillis,
4972583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                handle,
4982583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getHandlePresentation(),
4992583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                callerDisplayName,
5002583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getCallerDisplayNamePresentation(),
5012583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getGatewayInfo(),
502b78b27693afbe9736f0a54ec473328955251f885Ihab Awad                call.getTargetPhoneAccount(),
50312bccade85d58ca73d861aeba26ee25e97681895Tyler Gunn                includeVideoProvider ? call.getVideoProvider() : null,
5042583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                parentCallId,
5052583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                childCallIds,
5062583b67c376358538cfa11e25a241adb2dc9e34aSantos Cordon                call.getStatusHints(),
50712d61825cd0d6aa06272db3f77b5e30dce379951Santos Cordon                call.getVideoState(),
508a9d91da9fcf2d8223ec6e0ffb9bd2d506c5a25feNancy Chen                conferenceableCallIds,
509a9d91da9fcf2d8223ec6e0ffb9bd2d506c5a25feNancy Chen                call.getExtras());
5108c85dee84fbbd8c36cf1c7b061001c6129623f92Sailesh Nepal    }
51161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn
51261b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    /**
51361b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * Adds the call to the list of calls tracked by the {@link InCallController}.
51461b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     * @param call The call to add.
51561b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn     */
51661b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    private void addCall(Call call) {
51761b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        if (mCallIdMapper.getCallId(call) == null) {
51861b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            mCallIdMapper.addCall(call);
51961b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn            call.addListener(mCallListener);
52061b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn        }
52161b9210e5757db0cb49424fe917be0f788ea2218Tyler Gunn    }
522e3d76abbb6c7c726980abed522f6c3efcbb6019eSantos Cordon}
523