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 android.telephony.ims;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.TextUtils;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.Objects;
29 
30 /**
31  * Contains the information for SIP.
32  */
33 public final class SipDetails implements Parcelable {
34     /**
35      * Define a SIP method type related to this information.
36      * @hide
37      */
38     @Retention(RetentionPolicy.SOURCE)
39     @IntDef(prefix = "METHOD_", value = {
40             METHOD_UNKNOWN,
41             METHOD_REGISTER,
42             METHOD_PUBLISH,
43             METHOD_SUBSCRIBE
44     })
45     public @interface Method {}
46 
47     public static final int METHOD_UNKNOWN = 0;
48 
49     /**
50      * Indicates information related to the SIP registration method.
51      * See RFC 3261 for details.
52      */
53     public static final int METHOD_REGISTER = 1;
54     /**
55      * Indicates information related to the SIP publication method.
56      * See RFC 3903 for details.
57      */
58     public static final int METHOD_PUBLISH = 2;
59     /**
60      * Indicates information related to the SIP subscription method.
61      * See RFC 3856 for details.
62      */
63     public static final int METHOD_SUBSCRIBE = 3;
64 
65     /**
66      * Builder for creating {@link SipDetails} instances.
67      * @hide
68      */
69     public static final class Builder {
70         private int mMethod;
71         // Command Sequence value defined in RFC3261 Section 8.1.1.5
72         private int mCseq = 0;
73         private int mResponseCode = 0;
74         private String mResponsePhrase = "";
75         private int mReasonHeaderCause = 0;
76         private String mReasonHeaderText = "";
77         private @Nullable String mCallId;
78 
79         /**
80          * Build a new instance of {@link SipDetails}.
81          *
82          * @param method Indicates which SIP method this information is for.
83          */
Builder(@ethod int method)84         public Builder(@Method int method) {
85             this.mMethod = method;
86         }
87 
88         /**
89          * Sets the value of the CSeq header field for this SIP method.
90          * The CSeq header field serves as a way to identify and order transactions.
91          * It consists of a sequence number and a method.
92          * The method MUST match that of the request.
93          * Ref RFC3261 Section 8.1.1.5.
94          * @param cSeq The value of the CSeq header field.
95          * @return The same instance of the builder.
96          */
setCSeq(int cSeq)97         public @NonNull Builder setCSeq(int cSeq) {
98             this.mCseq = cSeq;
99             return this;
100         }
101 
102         /**
103          * Sets the SIP response code and reason response for this SIP method.
104          * Ref RFC3261 Section 21.
105          * @param responseCode The SIP response code sent from the network for the
106          * operation token specified.
107          * @param responsePhrase The optional reason response from the network. If
108          * there is a reason header included in the response, that should take
109          * precedence over the reason provided in the status line. If the network
110          * provided no reason with the SIP code, the string should be empty.
111          * @return The same instance of the builder.
112          */
setSipResponseCode(int responseCode, @NonNull String responsePhrase)113         public Builder setSipResponseCode(int responseCode,
114                 @NonNull String responsePhrase) {
115             this.mResponseCode = responseCode;
116             this.mResponsePhrase = responsePhrase;
117             return this;
118         }
119 
120 
121         /**
122          * Sets the detailed information of reason header for this SIP method.
123          * Ref RFC3326.
124          * @param reasonHeaderCause The “cause” parameter of the “reason”
125          * header included in the SIP message.
126          * @param reasonHeaderText The “text” parameter of the “reason”
127          * header included in the SIP message.
128          * @return The same instance of the builder.
129          */
setSipResponseReasonHeader(int reasonHeaderCause, @NonNull String reasonHeaderText)130         public Builder setSipResponseReasonHeader(int reasonHeaderCause,
131                 @NonNull String reasonHeaderText) {
132             this.mReasonHeaderCause = reasonHeaderCause;
133             this.mReasonHeaderText = reasonHeaderText;
134             return this;
135         }
136 
137 
138         /**
139          * Sets the value of the Call-ID header field for this SIP method.
140          * Ref RFC3261 Section 8.1.1.4.
141          * @param callId The value of the Call-ID header field.
142          * @return The same instance of the builder.
143          */
setCallId(@onNull String callId)144         public @NonNull Builder setCallId(@NonNull String callId) {
145             this.mCallId = callId;
146             return this;
147         }
148 
149         /**
150          * @return a new SipDetails from this Builder.
151          */
build()152         public @NonNull SipDetails build() {
153             return new SipDetails(this);
154         }
155     }
156 
157     private final int mMethod;
158     private final int mCseq;
159     private final int mResponseCode;
160     private final @NonNull String mResponsePhrase;
161     private final int mReasonHeaderCause;
162     private final @NonNull String mReasonHeaderText;
163     private final @Nullable String mCallId;
164 
SipDetails(Builder builder)165     private SipDetails(Builder builder) {
166         mMethod = builder.mMethod;
167         mCseq = builder.mCseq;
168         mResponseCode = builder.mResponseCode;
169         mResponsePhrase = builder.mResponsePhrase;
170         mReasonHeaderCause = builder.mReasonHeaderCause;
171         mReasonHeaderText = builder.mReasonHeaderText;
172         mCallId = builder.mCallId;
173     }
174 
175     /**
176      * Get the method type of this instance.
177      * @return The method type associated with this SIP information.
178      */
getMethod()179     public @Method int getMethod() {
180         return mMethod;
181     }
182 
183     /**
184      * Get the value of CSeq header field.
185      * The CSeq header field serves as a way to identify and order transactions.
186      * @return The command sequence value associated with this SIP information.
187      */
getCSeq()188     public int getCSeq() {
189         return mCseq;
190     }
191 
192     /**
193      * Get the value of response code from the SIP response.
194      * The SIP response code sent from the network for the operation token specified.
195      * @return The SIP response code associated with this SIP information.
196      */
getResponseCode()197     public int getResponseCode() {
198         return mResponseCode;
199     }
200 
201     /**
202      * Get the value of reason from the SIP response.
203      * The optional reason response from the network. If
204      * there is a reason header included in the response, that should take
205      * precedence over the reason provided in the status line.
206      * @return The optional reason response associated with this SIP information. If the network
207      *          provided no reason with the SIP code, the string should be empty.
208      */
getResponsePhrase()209     public @NonNull String getResponsePhrase() {
210         return mResponsePhrase;
211     }
212 
213     /**
214      * Get the "cause" parameter of the "reason" header.
215      * @return The "cause" parameter of the reason header. If the SIP message from the network
216      *          does not have a reason header, it should be 0.
217      */
getReasonHeaderCause()218     public int getReasonHeaderCause() {
219         return mReasonHeaderCause;
220     }
221 
222     /**
223      * Get the "text" parameter of the "reason" header in the SIP message.
224      * @return The "text" parameter of the reason header. If the SIP message from the network
225      *          does not have a reason header, it can be empty.
226      */
getReasonHeaderText()227     public @NonNull String getReasonHeaderText() {
228         return mReasonHeaderText;
229     }
230 
231     /**
232      * Get the value of the Call-ID header field for this SIP method.
233      * @return The Call-ID value associated with this SIP information. If the Call-ID value is
234      *          not set when ImsService notifies the framework, this value will be null.
235      */
getCallId()236     public @Nullable String getCallId() {
237         return mCallId;
238     }
239 
240     @Override
describeContents()241     public int describeContents() {
242         return 0;
243     }
244 
245     @Override
writeToParcel(@onNull Parcel dest, int flags)246     public void writeToParcel(@NonNull Parcel dest, int flags) {
247         dest.writeInt(mMethod);
248         dest.writeInt(mCseq);
249         dest.writeInt(mResponseCode);
250         dest.writeString(mResponsePhrase);
251         dest.writeInt(mReasonHeaderCause);
252         dest.writeString(mReasonHeaderText);
253         dest.writeString(mCallId);
254     }
255 
256     public static final @NonNull Creator<SipDetails> CREATOR =
257             new Creator<SipDetails>() {
258                 @Override
259                 public SipDetails createFromParcel(Parcel source) {
260                     return new SipDetails(source);
261                 }
262 
263                 @Override
264                 public SipDetails[] newArray(int size) {
265                     return new SipDetails[size];
266                 }
267             };
268 
269     /**
270      * Construct a SipDetails object from the given parcel.
271      */
SipDetails(Parcel in)272     private SipDetails(Parcel in) {
273         mMethod = in.readInt();
274         mCseq = in.readInt();
275         mResponseCode = in.readInt();
276         mResponsePhrase = in.readString();
277         mReasonHeaderCause = in.readInt();
278         mReasonHeaderText = in.readString();
279         mCallId = in.readString();
280     }
281 
282     @Override
equals(Object o)283     public boolean equals(Object o) {
284         if (this == o) return true;
285         if (o == null || getClass() != o.getClass()) return false;
286         SipDetails that = (SipDetails) o;
287         return mMethod == that.mMethod
288                 && mCseq == that.mCseq
289                 && mResponseCode == that.mResponseCode
290                 && TextUtils.equals(mResponsePhrase, that.mResponsePhrase)
291                 && mReasonHeaderCause == that.mReasonHeaderCause
292                 && TextUtils.equals(mReasonHeaderText, that.mReasonHeaderText)
293                 && TextUtils.equals(mCallId, that.mCallId);
294     }
295 
296     @Override
hashCode()297     public int hashCode() {
298         return Objects.hash(mMethod, mCseq, mResponseCode, mResponsePhrase, mReasonHeaderCause,
299                 mReasonHeaderText, mCallId);
300     }
301 
302     @Override
toString()303     public String toString() {
304         return "SipDetails { methodType= " + mMethod + ", cSeq=" + mCseq
305                 + ", ResponseCode=" + mResponseCode + ", ResponseCPhrase=" + mResponsePhrase
306                 + ", ReasonHeaderCause=" + mReasonHeaderCause
307                 + ", ReasonHeaderText=" + mReasonHeaderText + ", callId=" + mCallId + "}";
308     }
309 }
310