ImsExternalConnection.java revision 04c17b36e012335a8575ae24895fd3f0bea8491f
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.internal.telephony.imsphone;
18
19import com.android.internal.R;
20import com.android.internal.telephony.Call;
21import com.android.internal.telephony.CallStateException;
22import com.android.internal.telephony.Connection;
23import com.android.internal.telephony.Phone;
24import com.android.internal.telephony.PhoneConstants;
25import com.android.internal.telephony.UUSInfo;
26
27import android.content.Context;
28import android.net.Uri;
29import android.telecom.PhoneAccount;
30import android.telephony.PhoneNumberUtils;
31import android.telephony.ims.ImsExternalCallState;
32
33import java.util.Collections;
34import java.util.Set;
35import java.util.concurrent.ConcurrentHashMap;
36
37/**
38 * Represents an IMS call external to the device.  This class is used to represent a call which
39 * takes places on a secondary device associated with this one.  Originates from a Dialog Event
40 * Package.
41 *
42 * Dialog event package information is received from the IMS framework via
43 * {@link ImsExternalCallState} instances.
44 *
45 * @hide
46 */
47public class ImsExternalConnection extends Connection {
48
49    private static final String CONFERENCE_PREFIX = "conf";
50    private final Context mContext;
51
52    public interface Listener {
53        void onPullExternalCall(ImsExternalConnection connection);
54    }
55
56    /**
57     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
58     * load factor before resizing, 1 means we only expect a single thread to
59     * access the map so make only a single shard
60     */
61    private final Set<Listener> mListeners = Collections.newSetFromMap(
62            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
63
64    /**
65     * The unqiue dialog event package specified ID associated with this external connection.
66     */
67    private int mCallId;
68
69    /**
70     * A backing call associated with this external connection.
71     */
72    private ImsExternalCall mCall;
73
74    /**
75     * The original address as contained in the dialog event package.
76     */
77    private Uri mOriginalAddress;
78
79    /**
80     * Determines if the call is pullable.
81     */
82    private boolean mIsPullable;
83
84    protected ImsExternalConnection(Phone phone, int callId, Uri address, boolean isPullable) {
85        super(phone.getPhoneType());
86        mContext = phone.getContext();
87        mCall = new ImsExternalCall(phone, this);
88        mCallId = callId;
89        setExternalConnectionAddress(address);
90        mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
91        mIsPullable = isPullable;
92
93        rebuildCapabilities();
94        setActive();
95    }
96
97    /**
98     * @return the unique ID of this connection from the dialog event package data.
99     */
100    public int getCallId() {
101        return mCallId;
102    }
103
104    @Override
105    public Call getCall() {
106        return mCall;
107    }
108
109    @Override
110    public long getDisconnectTime() {
111        return 0;
112    }
113
114    @Override
115    public long getHoldDurationMillis() {
116        return 0;
117    }
118
119    @Override
120    public String getVendorDisconnectCause() {
121        return null;
122    }
123
124    @Override
125    public void hangup() throws CallStateException {
126        // No-op - Hangup is not supported for external calls.
127    }
128
129    @Override
130    public void separate() throws CallStateException {
131        // No-op - Separate is not supported for external calls.
132    }
133
134    @Override
135    public void proceedAfterWaitChar() {
136        // No-op - not supported for external calls.
137    }
138
139    @Override
140    public void proceedAfterWildChar(String str) {
141        // No-op - not supported for external calls.
142    }
143
144    @Override
145    public void cancelPostDial() {
146        // No-op - not supported for external calls.
147    }
148
149    @Override
150    public int getNumberPresentation() {
151        return mNumberPresentation;
152    }
153
154    @Override
155    public UUSInfo getUUSInfo() {
156        return null;
157    }
158
159    @Override
160    public int getPreciseDisconnectCause() {
161        return 0;
162    }
163
164    @Override
165    public boolean isMultiparty() {
166        return false;
167    }
168
169    /**
170     * Called by a {@link android.telecom.Connection} to indicate that this call should be pulled
171     * to the local device.
172     *
173     * Informs all listeners, in this case {@link ImsExternalCallTracker}, of the request to pull
174     * the call.
175     */
176    @Override
177    public void pullExternalCall() {
178        for (Listener listener : mListeners) {
179            listener.onPullExternalCall(this);
180        }
181    }
182
183    /**
184     * Sets this external call as active.
185     */
186    public void setActive() {
187        if (mCall == null) {
188            return;
189        }
190        mCall.setActive();
191    }
192
193    /**
194     * Sets this external call as terminated.
195     */
196    public void setTerminated() {
197        if (mCall == null) {
198            return;
199        }
200
201        mCall.setTerminated();
202    }
203
204    /**
205     * Changes whether the call can be pulled or not.
206     *
207     * @param isPullable {@code true} if the call can be pulled, {@code false} otherwise.
208     */
209    public void setIsPullable(boolean isPullable) {
210        mIsPullable = isPullable;
211        rebuildCapabilities();
212    }
213
214    /**
215     * Sets the address of this external connection.  Ensures that dialog event package SIP
216     * {@link Uri}s are converted to a regular telephone number.
217     *
218     * @param address The address from the dialog event package.
219     */
220    public void setExternalConnectionAddress(Uri address) {
221        mOriginalAddress = address;
222
223        if (PhoneAccount.SCHEME_SIP.equals(address.getScheme())) {
224            if (address.getSchemeSpecificPart().startsWith(CONFERENCE_PREFIX)) {
225                mCnapName = mContext.getString(com.android.internal.R.string.conference_call);
226                mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
227                mAddress = "";
228                mNumberPresentation = PhoneConstants.PRESENTATION_RESTRICTED;
229                return;
230            }
231        }
232        Uri telUri = PhoneNumberUtils.convertSipUriToTelUri(address);
233        mAddress = telUri.getSchemeSpecificPart();
234    }
235
236    public void addListener(Listener listener) {
237        mListeners.add(listener);
238    }
239
240    public void removeListener(Listener listener) {
241        mListeners.remove(listener);
242    }
243
244    /**
245     * Build a human representation of a connection instance, suitable for debugging.
246     * Don't log personal stuff unless in debug mode.
247     * @return a string representing the internal state of this connection.
248     */
249    public String toString() {
250        StringBuilder str = new StringBuilder(128);
251        str.append("[ImsExternalConnection dialogCallId:");
252        str.append(mCallId);
253        str.append(" state:");
254        if (mCall.getState() == Call.State.ACTIVE) {
255            str.append("Active");
256        } else if (mCall.getState() == Call.State.DISCONNECTED) {
257            str.append("Disconnected");
258        }
259        str.append("]");
260        return str.toString();
261    }
262
263    /**
264     * Rebuilds the connection capabilities.
265     */
266    private void rebuildCapabilities() {
267        int capabilities = Capability.IS_EXTERNAL_CONNECTION;
268        if (mIsPullable) {
269            capabilities |= Capability.IS_PULLABLE;
270        }
271
272        setConnectionCapabilities(capabilities);
273    }
274}
275