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