1 /*
2  * Copyright (C) 2014 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.services.telephony;
18 
19 import android.net.Uri;
20 import android.os.PersistableBundle;
21 import android.telecom.Connection;
22 import android.telecom.DisconnectCause;
23 import android.telecom.PhoneAccount;
24 import android.telephony.CarrierConfigManager;
25 import android.telephony.PhoneNumberUtils;
26 import android.telephony.SubscriptionInfo;
27 import android.text.TextUtils;
28 
29 import com.android.ims.internal.ConferenceParticipant;
30 import com.android.internal.telephony.Phone;
31 import com.android.internal.telephony.PhoneConstants;
32 import com.android.phone.PhoneGlobals;
33 import com.android.telephony.Rlog;
34 
35 import java.util.Locale;
36 
37 /**
38  * Represents a participant in a conference call.
39  */
40 public class ConferenceParticipantConnection extends Connection {
41 
42     private static final String LOG_TAG = "ConferenceParticipantConnection";
43 
44     private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
45     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
46 
47     /**
48      * The user entity URI For the conference participant.
49      */
50     private final Uri mUserEntity;
51 
52     /**
53      * The endpoint URI For the conference participant.
54      */
55     private final Uri mEndpoint;
56 
57     /**
58      * The connection which owns this participant.
59      */
60     private final com.android.internal.telephony.Connection mParentConnection;
61 
62     /**
63      * Creates a new instance.
64      *
65      * @param participant The conference participant to create the instance for.
66      * @param isRemotelyHosted {@code true} if this participant is part of a conference remotely
67      *                         hosted on another device, {@code false} otherwise.
68      */
ConferenceParticipantConnection( com.android.internal.telephony.Connection parentConnection, ConferenceParticipant participant, boolean isRemotelyHosted)69     public ConferenceParticipantConnection(
70             com.android.internal.telephony.Connection parentConnection,
71             ConferenceParticipant participant,
72             boolean isRemotelyHosted) {
73 
74         mParentConnection = parentConnection;
75 
76         int presentation = participant.getParticipantPresentation();
77         Uri address;
78         if (presentation != PhoneConstants.PRESENTATION_ALLOWED) {
79             address = null;
80         } else {
81             Phone phone = parentConnection.getCall().getPhone();
82             String countryIso = getCountryIso(phone);
83             address = ConferenceParticipant.getParticipantAddress(participant.getHandle(),
84                     countryIso);
85             if (address != null
86                     && isNeedParticipantPhoneNumberToNationalFormatForJp(phone, address)) {
87                 String number = PhoneNumberUtils.stripSeparators(
88                         PhoneNumberUtils.formatNumber(address.getSchemeSpecificPart(),
89                         JAPAN_ISO_COUNTRY_CODE));
90                 if (number != null) {
91                     address = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
92                 }
93             }
94         }
95         setAddress(address, presentation);
96         setVideoState(parentConnection.getVideoState());
97         setCallerDisplayName(participant.getDisplayName(), presentation);
98 
99         mUserEntity = participant.getHandle();
100         mEndpoint = participant.getEndpoint();
101 
102         setCapabilitiesAndProperties(isRemotelyHosted);
103     }
104 
105     /**
106      * Changes the state of the conference participant.
107      *
108      * @param newState The new state.
109      */
updateState(int newState)110     public void updateState(int newState) {
111         Log.v(this, "updateState endPoint: %s state: %s", Rlog.pii(LOG_TAG, mEndpoint),
112                 Connection.stateToString(newState));
113         if (newState == getState()) {
114             return;
115         }
116 
117         switch (newState) {
118             case STATE_INITIALIZING:
119                 setInitializing();
120                 break;
121             case STATE_RINGING:
122                 setRinging();
123                 break;
124             case STATE_DIALING:
125                 setDialing();
126                 break;
127             case STATE_HOLDING:
128                 setOnHold();
129                 break;
130             case STATE_ACTIVE:
131                 setActive();
132                 break;
133             case STATE_DISCONNECTED:
134                 setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
135                 destroy();
136                 break;
137             default:
138                 setActive();
139         }
140     }
141 
142     /**
143      * Disconnects the current {@code ConferenceParticipantConnection} from the conference.
144      * <p>
145      * Sends a participant disconnect signal to the associated parent connection.  The participant
146      * connection is not disconnected and cleaned up here.  On successful disconnection of the
147      * participant, the conference server will send an update to the conference controller
148      * indicating the disconnection was successful.
149      */
150     @Override
onDisconnect()151     public void onDisconnect() {
152         mParentConnection.onDisconnectConferenceParticipant(mUserEntity);
153     }
154 
155     /**
156      * Retrieves the user handle for this connection.
157      *
158      * @return The userEntity.
159      */
getUserEntity()160     public Uri getUserEntity() {
161         return mUserEntity;
162     }
163 
164     /**
165      * Retrieves the endpoint for this connection.
166      *
167      * @return The endpoint.
168      */
getEndpoint()169     public Uri getEndpoint() {
170         return mEndpoint;
171     }
172 
173     /**
174      * Configures the capabilities and properties applicable to this connection.  A
175      * conference participant can only be disconnected from a conference since there is not
176      * actual connection to the participant which could be split from the conference.
177      * @param isRemotelyHosted {@code true} if this participant is part of a conference hosted
178      *                         hosted on a remote device, {@code false} otherwise.
179      */
setCapabilitiesAndProperties(boolean isRemotelyHosted)180     private void setCapabilitiesAndProperties(boolean isRemotelyHosted) {
181         int capabilities = CAPABILITY_DISCONNECT_FROM_CONFERENCE;
182         setConnectionCapabilities(capabilities);
183 
184         if (isRemotelyHosted) {
185             setConnectionProperties(PROPERTY_REMOTELY_HOSTED);
186         }
187     }
188 
189     /**
190      * Given a {@link Phone} instance, determines the country ISO associated with the phone's
191      * subscription.
192      *
193      * @param phone The phone instance.
194      * @return The country ISO.
195      */
getCountryIso(Phone phone)196     private String getCountryIso(Phone phone) {
197         if (phone == null) {
198             return null;
199         }
200 
201         int subId = phone.getSubId();
202 
203         SubscriptionInfo subInfo = TelecomAccountRegistry.getInstance(null).
204                 getSubscriptionManager().getActiveSubscriptionInfo(subId);
205 
206         if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) {
207             return null;
208         }
209         // The SubscriptionInfo reports ISO country codes in lower case.  Convert to upper case,
210         // since ultimately we use this ISO when formatting the CEP phone number, and the phone
211         // number formatting library expects uppercase ISO country codes.
212         return subInfo.getCountryIso().toUpperCase(Locale.ROOT);
213     }
214 
215     /**
216      * Whether the Conference call participant number should be formatted to national number for
217      * Japan.
218      * @return {@code true} should be convert to the national format, {@code false} otherwise.
219      */
isNeedParticipantPhoneNumberToNationalFormatForJp(Phone phone, Uri uri)220     private boolean isNeedParticipantPhoneNumberToNationalFormatForJp(Phone phone, Uri uri) {
221         if (phone == null || uri == null) {
222             return false;
223         }
224         PersistableBundle bundle = PhoneGlobals.getInstance().getCarrierConfigForSubId(
225                 phone.getSubId());
226         return bundle != null && bundle.getBoolean(
227                 CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL)
228                 && uri.getSchemeSpecificPart().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN);
229     }
230 
231     /**
232      * Builds a string representation of this conference participant connection.
233      *
234      * @return String representation of connection.
235      */
236     @Override
toString()237     public String toString() {
238         StringBuilder sb = new StringBuilder();
239         sb.append("[ConferenceParticipantConnection objId:");
240         sb.append(System.identityHashCode(this));
241         sb.append(" endPoint:");
242         sb.append(Rlog.pii(LOG_TAG, mEndpoint));
243         sb.append(" address:");
244         sb.append(Rlog.pii(LOG_TAG, getAddress()));
245         sb.append(" addressPresentation:");
246         sb.append(getAddressPresentation());
247         sb.append(" parentConnection:");
248         sb.append(Rlog.pii(LOG_TAG, mParentConnection.getAddress()));
249         sb.append(" state:");
250         sb.append(Connection.stateToString(getState()));
251         sb.append(" connectTime:");
252         sb.append(getConnectTimeMillis());
253         sb.append(" connectElapsedTime:");
254         sb.append(getConnectionStartElapsedRealtimeMillis());
255         sb.append("]");
256 
257         return sb.toString();
258     }
259 }
260