1fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn/*
2fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * Copyright (C) 2016 The Android Open Source Project
3fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn *
4fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * Licensed under the Apache License, Version 2.0 (the "License");
5fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * you may not use this file except in compliance with the License.
6fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * You may obtain a copy of the License at
7fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn *
8fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn *      http://www.apache.org/licenses/LICENSE-2.0
9fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn *
10fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * Unless required by applicable law or agreed to in writing, software
11fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * distributed under the License is distributed on an "AS IS" BASIS,
12fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * See the License for the specific language governing permissions and
14fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * limitations under the License
15fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn */
16fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
17fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnpackage com.android.internal.telephony.imsphone;
18fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
1993da8529345711fd1a5617f3c8b15c0921eececbTyler Gunnimport com.android.ims.ImsCallProfile;
20fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport com.android.ims.ImsExternalCallState;
2193da8529345711fd1a5617f3c8b15c0921eececbTyler Gunnimport com.android.ims.ImsExternalCallStateListener;
2267f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunnimport com.android.internal.annotations.VisibleForTesting;
23fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport com.android.internal.telephony.Call;
24fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport com.android.internal.telephony.Connection;
25fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport com.android.internal.telephony.Phone;
2691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport com.android.internal.telephony.PhoneConstants;
27fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
2891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport android.os.AsyncResult;
29fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport android.os.Bundle;
3091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport android.os.Handler;
3191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport android.os.Message;
32fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport android.telecom.PhoneAccountHandle;
3391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport android.telecom.VideoProfile;
3491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnimport android.telephony.TelephonyManager;
35fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport android.util.ArrayMap;
36fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport android.util.Log;
37fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
38fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport java.util.Iterator;
39fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport java.util.List;
40fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunnimport java.util.Map;
41fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
42fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn/**
43fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn * Responsible for tracking external calls known to the system.
44fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn */
4591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunnpublic class ImsExternalCallTracker implements ImsPhoneCallTracker.PhoneStateListener {
46fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
4793da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    /**
4867f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     * Interface implemented by modules which are capable of notifying interested parties of new
4967f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     * unknown connections, and changes to call state.
5067f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     * This is used to break the dependency between {@link ImsExternalCallTracker} and
5167f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     * {@link ImsPhone}.
5267f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     *
5367f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     * @hide
5467f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn     */
5567f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    public static interface ImsCallNotify {
5667f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        /**
5767f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn         * Notifies that an unknown connection has been added.
5867f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn         * @param c The new unknown connection.
5967f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn         */
6067f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        void notifyUnknownConnection(Connection c);
6167f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn
6267f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        /**
6367f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn         * Notifies of a change to call state.
6467f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn         */
6567f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        void notifyPreciseCallStateChanged();
6667f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    }
6767f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn
6867f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn
6967f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    /**
7093da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn     * Implements the {@link ImsExternalCallStateListener}, which is responsible for receiving
7193da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn     * external call state updates from the IMS framework.
7293da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn     */
7393da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    public class ExternalCallStateListener extends ImsExternalCallStateListener {
7493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        @Override
7593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        public void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallState) {
7693da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn            refreshExternalCallState(externalCallState);
7793da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        }
7893da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    }
7993da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn
8093da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    /**
8193da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn     * Receives callbacks from {@link ImsExternalConnection}s when a call pull has been initiated.
8293da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn     */
8393da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    public class ExternalConnectionListener implements ImsExternalConnection.Listener {
8493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        @Override
8593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        public void onPullExternalCall(ImsExternalConnection connection) {
8693da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn            Log.d(TAG, "onPullExternalCall: connection = " + connection);
8791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            if (mCallPuller == null) {
8891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                Log.e(TAG, "onPullExternalCall : No call puller defined");
8991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                return;
9091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            }
913de018282cd7dae66d4dfff41303e81419d2004dTyler Gunn            mCallPuller.pullExternalCall(connection.getAddress(), connection.getVideoState(),
923de018282cd7dae66d4dfff41303e81419d2004dTyler Gunn                    connection.getCallId());
9393da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        }
9493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    }
9593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn
96fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    public final static String TAG = "ImsExternalCallTracker";
97fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
9891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private static final int EVENT_VIDEO_CAPABILITIES_CHANGED = 1;
9991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
100fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
101fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Extra key used when informing telecom of a new external call using the
102fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * {@link android.telecom.TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)} API.
103fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Used to ensure that when Telecom requests the {@link android.telecom.ConnectionService} to
104fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * create the connection for the unknown call that we can determine which
105fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * {@link ImsExternalConnection} in {@link #mExternalConnections} is the one being requested.
106fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
107fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    public final static String EXTRA_IMS_EXTERNAL_CALL_ID =
108fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            "android.telephony.ImsExternalCallTracker.extra.EXTERNAL_CALL_ID";
109fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
110fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
11191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Contains a list of the external connections known by the ImsExternalCallTracker.  These are
112fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * connections which originated from a dialog event package and reside on another device.
113fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Used in multi-endpoint (VoLTE for internet connected endpoints) scenarios.
114fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
115fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    private Map<Integer, ImsExternalConnection> mExternalConnections =
11693da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn            new ArrayMap<>();
11791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
11891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
11991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Tracks whether each external connection tracked in
12091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * {@link #mExternalConnections} can be pulled, as reported by the latest dialog event package
12191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * received from the network.  We need to know this because the pull state of a call can be
12291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * overridden based on the following factors:
12391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * 1) An external video call cannot be pulled if the current device does not have video
12491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *    capability.
12591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * 2) If the device has any active or held calls locally, no external calls may be pulled to
12691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *    the local device.
12791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
12891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private Map<Integer, Boolean> mExternalCallPullableState = new ArrayMap<>();
12993da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    private final ImsPhone mPhone;
13067f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    private final ImsCallNotify mCallStateNotifier;
13193da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    private final ExternalCallStateListener mExternalCallStateListener;
13293da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    private final ExternalConnectionListener mExternalConnectionListener =
13393da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn            new ExternalConnectionListener();
13491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private ImsPullCall mCallPuller;
13591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private boolean mIsVideoCapable;
13691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private boolean mHasActiveCalls;
13791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
13891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private final Handler mHandler = new Handler() {
13991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        @Override
14091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        public void handleMessage(Message msg) {
14191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            switch (msg.what) {
14291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                case EVENT_VIDEO_CAPABILITIES_CHANGED:
14391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    handleVideoCapabilitiesChanged((AsyncResult) msg.obj);
14491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    break;
14591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                default:
14691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    break;
14791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            }
14891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
14991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    };
15093da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn
15167f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    @VisibleForTesting
15267f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    public ImsExternalCallTracker(ImsPhone phone, ImsPullCall callPuller,
15367f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            ImsCallNotify callNotifier) {
15467f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn
15567f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mPhone = phone;
15667f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mCallStateNotifier = callNotifier;
15767f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mExternalCallStateListener = new ExternalCallStateListener();
15867f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mCallPuller = callPuller;
15967f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn    }
16091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
16191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    public ImsExternalCallTracker(ImsPhone phone) {
162fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        mPhone = phone;
16367f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mCallStateNotifier = new ImsCallNotify() {
16467f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            @Override
16567f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            public void notifyUnknownConnection(Connection c) {
16667f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn                mPhone.notifyUnknownConnection(c);
16767f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            }
16867f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn
16967f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            @Override
17067f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            public void notifyPreciseCallStateChanged() {
17167f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn                mPhone.notifyPreciseCallStateChanged();
17267f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            }
17367f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        };
17493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        mExternalCallStateListener = new ExternalCallStateListener();
17591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        registerForNotifications();
17691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
17791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
17891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
17991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Performs any cleanup required before the ImsExternalCallTracker is destroyed.
18091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
18191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    public void tearDown() {
18291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        unregisterForNotifications();
18391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
18491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
18591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
18691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Sets the implementation of {@link ImsPullCall} which is responsible for pulling calls.
18791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *
18891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param callPuller The pull call implementation.
18991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
19091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    public void setCallPuller(ImsPullCall callPuller) {
19191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn       mCallPuller = callPuller;
19293da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    }
19393da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn
19493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn    public ExternalCallStateListener getExternalCallStateListener() {
19593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        return mExternalCallStateListener;
196fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
197fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
198fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
19991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Handles changes to the phone state as notified by the {@link ImsPhoneCallTracker}.
20091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *
20191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param oldState The previous phone state.
20291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param newState The new phone state.
20391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
20491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    @Override
20591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    public void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState) {
20691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        mHasActiveCalls = newState != PhoneConstants.State.IDLE;
20791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.i(TAG, "onPhoneStateChanged : hasActiveCalls = " + mHasActiveCalls);
20891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
20991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        refreshCallPullState();
21091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
21191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
21291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
21391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Registers for video capability changes.
21491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
21591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private void registerForNotifications() {
21691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        if (mPhone != null) {
21791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            Log.d(TAG, "Registering: " + mPhone);
21891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            mPhone.getDefaultPhone().registerForVideoCapabilityChanged(mHandler,
21991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    EVENT_VIDEO_CAPABILITIES_CHANGED, null);
22091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
22191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
22291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
22391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
22491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Unregisters for video capability changes.
22591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
22691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private void unregisterForNotifications() {
22791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        if (mPhone != null) {
22891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            Log.d(TAG, "Unregistering: " + mPhone);
22991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            mPhone.unregisterForVideoCapabilityChanged(mHandler);
23091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
23191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
23291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
23391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
23491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
235fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Called when the IMS stack receives a new dialog event package.  Triggers the creation and
236fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * update of {@link ImsExternalConnection}s to represent the dialogs in the dialog event
237fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * package data.
238fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *
239fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param externalCallStates the {@link ImsExternalCallState} information for the dialog event
240fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *                           package.
241fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
242fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    public void refreshExternalCallState(List<ImsExternalCallState> externalCallStates) {
243da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn        Log.d(TAG, "refreshExternalCallState");
244fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
245fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // Check to see if any call Ids are no longer present in the external call state.  If they
246fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // are, the calls are terminated and should be removed.
247fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        Iterator<Map.Entry<Integer, ImsExternalConnection>> connectionIterator =
248fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                mExternalConnections.entrySet().iterator();
249fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        boolean wasCallRemoved = false;
250fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        while (connectionIterator.hasNext()) {
251fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            Map.Entry<Integer, ImsExternalConnection> entry = connectionIterator.next();
252fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            int callId = entry.getKey().intValue();
253fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
254fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            if (!containsCallId(externalCallStates, callId)) {
255fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                ImsExternalConnection externalConnection = entry.getValue();
256fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                externalConnection.setTerminated();
25793da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn                externalConnection.removeListener(mExternalConnectionListener);
258fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                connectionIterator.remove();
259fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                wasCallRemoved = true;
260fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            }
261fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        }
262fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // If one or more calls were removed, trigger a notification that will cause the
263fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // TelephonyConnection instancse to refresh their state with Telecom.
264fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        if (wasCallRemoved) {
26567f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn            mCallStateNotifier.notifyPreciseCallStateChanged();
266fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        }
267fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
268fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // Check for new calls, and updates to existing ones.
269da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn        if (externalCallStates != null && !externalCallStates.isEmpty()) {
270da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn            for (ImsExternalCallState callState : externalCallStates) {
271da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                if (!mExternalConnections.containsKey(callState.getCallId())) {
272da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    Log.d(TAG, "refreshExternalCallState: got = " + callState);
273da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    // If there is a new entry and it is already terminated, don't bother adding it to
274da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    // telecom.
275da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    if (callState.getCallState() != ImsExternalCallState.CALL_STATE_CONFIRMED) {
276da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                        continue;
277da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    }
278da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    createExternalConnection(callState);
279da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                } else {
280da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                    updateExistingConnection(mExternalConnections.get(callState.getCallId()),
281da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn                            callState);
282fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                }
283fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            }
284fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        }
285fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
286fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
287fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
288fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Finds an external connection given a call Id.
289fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *
290fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param callId The call Id.
291fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @return The {@link Connection}, or {@code null} if no match found.
292fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
293fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    public Connection getConnectionById(int callId) {
294fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        return mExternalConnections.get(callId);
295fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
296fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
297fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
298fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Given an {@link ImsExternalCallState} instance obtained from a dialog event package,
299fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * creates a new instance of {@link ImsExternalConnection} to represent the connection, and
300fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * initiates the addition of the new call to Telecom as an unknown call.
301fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *
302fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param state External call state from a dialog event package.
303fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
304fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    private void createExternalConnection(ImsExternalCallState state) {
30591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.i(TAG, "createExternalConnection : state = " + state);
30691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
30791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        int videoState = ImsCallProfile.getVideoStateFromCallType(state.getCallType());
308fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
30991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        boolean isCallPullPermitted = isCallPullPermitted(state.isCallPullable(), videoState);
310fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        ImsExternalConnection connection = new ImsExternalConnection(mPhone,
311fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                state.getCallId(), /* Dialog event package call id */
3121c23d391a314ffdb71874b06e9a0e54607208832Tyler Gunn                state.getAddress() /* phone number */,
31391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                isCallPullPermitted);
31491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        connection.setVideoState(videoState);
31593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        connection.addListener(mExternalConnectionListener);
316fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
31791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.d(TAG,
31891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                "createExternalConnection - pullable state : externalCallId = "
31991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + connection.getCallId()
32091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; isPullable = " + isCallPullPermitted
32191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; networkPullable = " + state.isCallPullable()
32291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; isVideo = " + VideoProfile.isVideo(videoState)
32391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; videoEnabled = " + mIsVideoCapable
32491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; hasActiveCalls = " + mHasActiveCalls);
32591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
326fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // Add to list of tracked connections.
327fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        mExternalConnections.put(connection.getCallId(), connection);
328814442c3754ef76d896247aa72cf0b050c6c0f29Tyler Gunn        mExternalCallPullableState.put(connection.getCallId(), state.isCallPullable());
329fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
330fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // Note: The notification of unknown connection is ultimately handled by
331fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // PstnIncomingCallNotifier#addNewUnknownCall.  That method will ensure that an extra is set
332fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // containing the ImsExternalConnection#mCallId so that we have a means of reconciling which
333fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        // unknown call was added.
33467f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn        mCallStateNotifier.notifyUnknownConnection(connection);
335fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
336fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
337fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
338fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Given an existing {@link ImsExternalConnection}, applies any changes found found in a
339fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * {@link ImsExternalCallState} instance received from a dialog event package to the connection.
340fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *
341fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param connection The connection to apply changes to.
342fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param state The new dialog state for the connection.
343fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
344fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    private void updateExistingConnection(ImsExternalConnection connection,
345fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            ImsExternalCallState state) {
34691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
34791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.i(TAG, "updateExistingConnection : state = " + state);
348fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        Call.State existingState = connection.getState();
349fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        Call.State newState = state.getCallState() == ImsExternalCallState.CALL_STATE_CONFIRMED ?
350fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                Call.State.ACTIVE : Call.State.DISCONNECTED;
351fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
352fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        if (existingState != newState) {
353fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            if (newState == Call.State.ACTIVE) {
354fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                connection.setActive();
355fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            } else {
356fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                connection.setTerminated();
35793da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn                connection.removeListener(mExternalConnectionListener);
35891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                mExternalConnections.remove(connection.getCallId());
35991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                mExternalCallPullableState.remove(connection.getCallId());
36067f3535dbfa1e4ca1ed846dd649015e8bd1f7328Tyler Gunn                mCallStateNotifier.notifyPreciseCallStateChanged();
361fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            }
362fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        }
363fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
36493da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        int newVideoState = ImsCallProfile.getVideoStateFromCallType(state.getCallType());
36593da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        if (newVideoState != connection.getVideoState()) {
36693da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn            connection.setVideoState(newVideoState);
36793da8529345711fd1a5617f3c8b15c0921eececbTyler Gunn        }
36891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
369814442c3754ef76d896247aa72cf0b050c6c0f29Tyler Gunn        mExternalCallPullableState.put(state.getCallId(), state.isCallPullable());
37091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        boolean isCallPullPermitted = isCallPullPermitted(state.isCallPullable(), newVideoState);
37191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.d(TAG,
372814442c3754ef76d896247aa72cf0b050c6c0f29Tyler Gunn                "updateExistingConnection - pullable state : externalCallId = " + connection
373814442c3754ef76d896247aa72cf0b050c6c0f29Tyler Gunn                        .getCallId()
37491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; isPullable = " + isCallPullPermitted
37591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; networkPullable = " + state.isCallPullable()
37691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; isVideo = "
37791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + VideoProfile.isVideo(connection.getVideoState())
37891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; videoEnabled = " + mIsVideoCapable
37991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                        + " ; hasActiveCalls = " + mHasActiveCalls);
38091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
38191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        connection.setIsPullable(isCallPullPermitted);
38291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
38391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
38491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
38591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Update whether the external calls known can be pulled.  Combines the last known network
38691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * pullable state with local device conditions to determine if each call can be pulled.
38791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
38891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private void refreshCallPullState() {
38991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.d(TAG, "refreshCallPullState");
39091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
39191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        for (ImsExternalConnection imsExternalConnection : mExternalConnections.values()) {
39291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            boolean isNetworkPullable =
39391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    mExternalCallPullableState.get(imsExternalConnection.getCallId())
39491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            .booleanValue();
39591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            boolean isCallPullPermitted =
39691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    isCallPullPermitted(isNetworkPullable, imsExternalConnection.getVideoState());
39791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            Log.d(TAG,
39891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                    "refreshCallPullState : externalCallId = " + imsExternalConnection.getCallId()
39991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + " ; isPullable = " + isCallPullPermitted
40091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + " ; networkPullable = " + isNetworkPullable
40191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + " ; isVideo = "
40291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + VideoProfile.isVideo(imsExternalConnection.getVideoState())
40391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + " ; videoEnabled = " + mIsVideoCapable
40491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn                            + " ; hasActiveCalls = " + mHasActiveCalls);
40591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            imsExternalConnection.setIsPullable(isCallPullPermitted);
40691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
407fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
408fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
409fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    /**
410fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * Determines if a list of call states obtained from a dialog event package contacts an existing
411fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * call Id.
412fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     *
413fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param externalCallStates The dialog event package state information.
414fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @param callId The call Id.
415fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     * @return {@code true} if the state information contains the call Id, {@code false} otherwise.
416fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn     */
417fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    private boolean containsCallId(List<ImsExternalCallState> externalCallStates, int callId) {
418da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn        if (externalCallStates == null) {
419da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn            return false;
420da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn        }
421da8286bfe1baf68d81a778031d05c3d13fa03f7cTyler Gunn
422fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        for (ImsExternalCallState state : externalCallStates) {
423fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            if (state.getCallId() == callId) {
424fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn                return true;
425fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn            }
426fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        }
427fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn
428fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn        return false;
429fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn    }
43091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
43191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
43291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Handles a change to the video capabilities reported by
43391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * {@link Phone#notifyForVideoCapabilityChanged(boolean)}.
43491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *
43591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param ar The AsyncResult containing the new video capability of the device.
43691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
43791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private void handleVideoCapabilitiesChanged(AsyncResult ar) {
43891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        mIsVideoCapable = (Boolean) ar.result;
43991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        Log.i(TAG, "handleVideoCapabilitiesChanged : isVideoCapable = " + mIsVideoCapable);
44091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
44191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        // Refresh pullable state if video capability changed.
44291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        refreshCallPullState();
44391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
44491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
44591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    /**
44691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * Determines whether an external call can be pulled based on the pullability state enforced
44791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * by the network, as well as local device rules.
44891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *
44991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param isNetworkPullable {@code true} if the network indicates the call can be pulled,
45091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     *      {@code false} otherwise.
45191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @param videoState the VideoState of the external call.
45291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     * @return {@code true} if the external call can be pulled, {@code false} otherwise.
45391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn     */
45491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    private boolean isCallPullPermitted(boolean isNetworkPullable, int videoState) {
45591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        if (VideoProfile.isVideo(videoState) && !mIsVideoCapable) {
45691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            // If the external call is a video call and the local device does not have video
45791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            // capability at this time, it cannot be pulled.
45891c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            return false;
45991c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
46091c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
46191c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        if (mHasActiveCalls) {
46291c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            // If there are active calls on the local device, the call cannot be pulled.
46391c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn            return false;
46491c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        }
46591c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn
46691c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn        return isNetworkPullable;
46791c714c06cc426b4f1af05a758c4d560b16f6d24Tyler Gunn    }
468fec523bdc5d2b6dd90212c8076bf34bcc2ab08b2Tyler Gunn}
469