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