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