1 /* 2 * Copyright (C) 2022 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; 18 19 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE; 20 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING; 21 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING; 22 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING; 23 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING; 24 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP; 25 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING; 26 27 import android.net.Uri; 28 import android.telephony.Annotation.PreciseCallStates; 29 import android.telephony.ims.ImsCallProfile; 30 import android.telephony.ims.ImsStreamMediaProfile; 31 import android.text.TextUtils; 32 33 import com.android.ims.internal.ConferenceParticipant; 34 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 35 import com.android.telephony.Rlog; 36 37 /** 38 * Connection information for SRVCC 39 */ 40 public class SrvccConnection { 41 private static final String TAG = "SrvccConnection"; 42 43 public static final int CALL_TYPE_NORMAL = 0; 44 public static final int CALL_TYPE_EMERGENCY = 1; 45 46 public static final int SUBSTATE_NONE = 0; 47 /** Pre-alerting state. Applicable for MT calls only */ 48 public static final int SUBSTATE_PREALERTING = 1; 49 50 public static final int TONE_NONE = 0; 51 public static final int TONE_LOCAL = 1; 52 public static final int TONE_NETWORK = 2; 53 54 /** Values are CALL_TYPE_ */ 55 private int mType = CALL_TYPE_NORMAL; 56 57 /** Values are Call.State */ 58 private Call.State mState; 59 60 /** Values are SUBSTATE_ */ 61 private int mSubstate = SUBSTATE_NONE; 62 63 /** Values are TONE_ */ 64 private int mRingbackToneType = TONE_NONE; 65 66 /** true if it is a multi-party call */ 67 private boolean mIsMpty = false; 68 69 /** true if it is a mobile terminated call */ 70 private boolean mIsMT; 71 72 /** Remote party nummber */ 73 private String mNumber; 74 75 /** Values are PhoneConstants.PRESENTATION_ */ 76 private int mNumPresentation; 77 78 /** Remote party name */ 79 private String mName; 80 81 /** Values are PhoneConstants.PRESENTATION_ */ 82 private int mNamePresentation; 83 SrvccConnection(ImsCallProfile profile, ImsPhoneConnection c, @PreciseCallStates int preciseCallState)84 public SrvccConnection(ImsCallProfile profile, 85 ImsPhoneConnection c, @PreciseCallStates int preciseCallState) { 86 mState = toCallState(preciseCallState); 87 if (mState == Call.State.ALERTING) { 88 mRingbackToneType = isLocalTone(profile) ? TONE_LOCAL : TONE_NETWORK; 89 } 90 if (preciseCallState == PRECISE_CALL_STATE_INCOMING_SETUP) { 91 mSubstate = SUBSTATE_PREALERTING; 92 } 93 94 if (c == null) { 95 initialize(profile); 96 } else { 97 initialize(c); 98 } 99 } 100 SrvccConnection(ConferenceParticipant cp, @PreciseCallStates int preciseCallState)101 public SrvccConnection(ConferenceParticipant cp, @PreciseCallStates int preciseCallState) { 102 Rlog.d(TAG, "initialize with ConferenceParticipant"); 103 mState = toCallState(preciseCallState); 104 mIsMT = cp.getCallDirection() == android.telecom.Call.Details.DIRECTION_INCOMING; 105 mNumber = getParticipantAddress(cp.getHandle()); 106 mNumPresentation = cp.getParticipantPresentation(); 107 if (mNumPresentation == PhoneConstants.PRESENTATION_RESTRICTED) { 108 mNumber = ""; 109 } 110 mName = cp.getDisplayName(); 111 if (!TextUtils.isEmpty(mName)) { 112 mNamePresentation = PhoneConstants.PRESENTATION_ALLOWED; 113 } else { 114 mNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN; 115 } 116 mIsMpty = true; 117 } 118 getParticipantAddress(Uri address)119 private static String getParticipantAddress(Uri address) { 120 if (address == null) { 121 return null; 122 } 123 124 String number = address.getSchemeSpecificPart(); 125 if (TextUtils.isEmpty(number)) { 126 return null; 127 } 128 129 String[] numberParts = number.split("[@;:]"); 130 if (numberParts.length == 0) return null; 131 132 return numberParts[0]; 133 } 134 135 // MT call in alerting or prealerting state initialize(ImsCallProfile profile)136 private void initialize(ImsCallProfile profile) { 137 Rlog.d(TAG, "initialize with ImsCallProfile"); 138 mIsMT = true; 139 mNumber = profile.getCallExtra(ImsCallProfile.EXTRA_OI); 140 mName = profile.getCallExtra(ImsCallProfile.EXTRA_CNA); 141 mNumPresentation = ImsCallProfile.OIRToPresentation( 142 profile.getCallExtraInt(ImsCallProfile.EXTRA_OIR)); 143 mNamePresentation = ImsCallProfile.OIRToPresentation( 144 profile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP)); 145 } 146 initialize(ImsPhoneConnection c)147 private void initialize(ImsPhoneConnection c) { 148 Rlog.d(TAG, "initialize with ImsPhoneConnection"); 149 if (c.isEmergencyCall()) { 150 mType = CALL_TYPE_EMERGENCY; 151 } 152 mIsMT = c.isIncoming(); 153 mNumber = c.getAddress(); 154 mNumPresentation = c.getNumberPresentation(); 155 mName = c.getCnapName(); 156 mNamePresentation = c.getCnapNamePresentation(); 157 } 158 isLocalTone(ImsCallProfile profile)159 private boolean isLocalTone(ImsCallProfile profile) { 160 if (profile == null) return false; 161 162 ImsStreamMediaProfile mediaProfile = profile.getMediaProfile(); 163 if (mediaProfile == null) return false; 164 165 boolean shouldPlayRingback = 166 (mediaProfile.getAudioDirection() == ImsStreamMediaProfile.DIRECTION_INACTIVE) 167 ? true : false; 168 return shouldPlayRingback; 169 } 170 toCallState(int preciseCallState)171 private static Call.State toCallState(int preciseCallState) { 172 switch (preciseCallState) { 173 case PRECISE_CALL_STATE_ACTIVE: return Call.State.ACTIVE; 174 case PRECISE_CALL_STATE_HOLDING: return Call.State.HOLDING; 175 case PRECISE_CALL_STATE_DIALING: return Call.State.DIALING; 176 case PRECISE_CALL_STATE_ALERTING: return Call.State.ALERTING; 177 case PRECISE_CALL_STATE_INCOMING: return Call.State.INCOMING; 178 case PRECISE_CALL_STATE_WAITING: return Call.State.WAITING; 179 case PRECISE_CALL_STATE_INCOMING_SETUP: return Call.State.INCOMING; 180 default: 181 } 182 return Call.State.DISCONNECTED; 183 } 184 185 /** Returns the type of the call */ getType()186 public int getType() { 187 return mType; 188 } 189 190 /** Returns the state */ getState()191 public Call.State getState() { 192 return mState; 193 } 194 195 /** Updates the state */ setState(Call.State state)196 public void setState(Call.State state) { 197 mState = state; 198 } 199 200 /** Returns the sub state */ getSubState()201 public int getSubState() { 202 return mSubstate; 203 } 204 205 /** Returns the ringback tone type */ getRingbackToneType()206 public int getRingbackToneType() { 207 return mRingbackToneType; 208 } 209 210 /** true if it is a multi-party call */ isMultiParty()211 public boolean isMultiParty() { 212 return mIsMpty; 213 } 214 215 /** true if it is a mobile terminated call */ isIncoming()216 public boolean isIncoming() { 217 return mIsMT; 218 } 219 220 /** Returns the remote party nummber */ getNumber()221 public String getNumber() { 222 return mNumber; 223 } 224 225 /** Returns the number presentation */ getNumberPresentation()226 public int getNumberPresentation() { 227 return mNumPresentation; 228 } 229 230 /** Returns the remote party name */ getName()231 public String getName() { 232 return mName; 233 } 234 235 /** Returns the name presentation */ getNamePresentation()236 public int getNamePresentation() { 237 return mNamePresentation; 238 } 239 240 /** 241 * Build a human representation of a connection instance, suitable for debugging. 242 * Don't log personal stuff unless in debug mode. 243 * @return a string representing the internal state of this connection. 244 */ 245 @Override toString()246 public String toString() { 247 StringBuilder sb = new StringBuilder(); 248 sb.append(" type:").append(getType()); 249 sb.append(", state:").append(getState()); 250 sb.append(", subState:").append(getSubState()); 251 sb.append(", toneType:").append(getRingbackToneType()); 252 sb.append(", mpty:").append(isMultiParty()); 253 sb.append(", incoming:").append(isIncoming()); 254 sb.append(", numberPresentation:").append(getNumberPresentation()); 255 sb.append(", number:").append(Rlog.pii(TAG, getNumber())); 256 sb.append(", namePresentation:").append(getNamePresentation()); 257 sb.append(", name:").append(Rlog.pii(TAG, getName())); 258 return sb.toString(); 259 } 260 } 261