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.telecom;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.text.TextUtils;
24 
25 import androidx.annotation.NonNull;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 
30 /**
31  * This class defines exceptions that can be thrown when using Telecom APIs with
32  * {@link android.os.OutcomeReceiver}s.  Most of these exceptions are thrown when changing a call
33  * state with {@link CallControl}s or {@link CallControlCallback}s.
34  */
35 public final class CallException extends RuntimeException implements Parcelable {
36     /** @hide **/
37     public static final String TRANSACTION_EXCEPTION_KEY = "TelecomTransactionalExceptionKey";
38 
39     /**
40      * The operation has failed due to an unknown or unspecified error.
41      */
42     public static final int CODE_ERROR_UNKNOWN = 1;
43 
44     /**
45      * The operation has failed due to Telecom failing to hold the current active call for the
46      * call attempting to become the new active call.  The client should end the current active call
47      * and re-try the failed operation.
48      */
49     public static final int CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL = 2;
50 
51     /**
52      * The operation has failed because Telecom has already removed the call from the server side
53      * and destroyed all the objects associated with it.  The client should re-add the call.
54      */
55     public static final int CODE_CALL_IS_NOT_BEING_TRACKED = 3;
56 
57     /**
58      * The operation has failed because Telecom cannot set the requested call as the current active
59      * call.  The client should end the current active call and re-try the operation.
60      */
61     public static final int CODE_CALL_CANNOT_BE_SET_TO_ACTIVE = 4;
62 
63     /**
64      * The operation has failed because there is either no PhoneAccount registered with Telecom
65      * for the given operation, or the limit of calls has been reached. The client should end the
66      * current active call and re-try the failed operation.
67      */
68     public static final int CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME = 5;
69 
70     /**
71      * The operation has failed because the operation failed to complete before the timeout
72      */
73     public static final int CODE_OPERATION_TIMED_OUT = 6;
74 
75     private int mCode = CODE_ERROR_UNKNOWN;
76     private final String mMessage;
77 
78     @Override
describeContents()79     public int describeContents() {
80         return 0;
81     }
82 
83     @Override
writeToParcel(@onNull Parcel dest, int flags)84     public void writeToParcel(@NonNull Parcel dest, int flags) {
85         dest.writeString8(mMessage);
86         dest.writeInt(mCode);
87     }
88 
89     /**
90      * Responsible for creating CallAttribute objects for deserialized Parcels.
91      */
92     public static final @android.annotation.NonNull
93             Parcelable.Creator<CallException> CREATOR = new Parcelable.Creator<>() {
94                     @Override
95                     public CallException createFromParcel(Parcel source) {
96                         return new CallException(source.readString8(), source.readInt());
97                     }
98 
99                     @Override
100                     public CallException[] newArray(int size) {
101                         return new CallException[size];
102                     }
103             };
104 
105     /** @hide */
106     @Retention(RetentionPolicy.SOURCE)
107     @IntDef(prefix = "CODE_ERROR_", value = {
108             CODE_ERROR_UNKNOWN,
109             CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL,
110             CODE_CALL_IS_NOT_BEING_TRACKED,
111             CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
112             CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
113             CODE_OPERATION_TIMED_OUT
114     })
115     public @interface CallErrorCode {
116     }
117 
118     /**
119      * Constructor for a new CallException that has a defined error code in this class
120      *
121      * @param message related to why the exception was created
122      * @param code defined above that caused this exception to be created
123      */
CallException(@ullable String message, @CallErrorCode int code)124     public CallException(@Nullable String message, @CallErrorCode int code) {
125         super(getMessage(message, code));
126         mCode = code;
127         mMessage = message;
128     }
129 
130     /**
131      * @return one of the error codes defined in this class that was passed into the constructor
132      */
getCode()133     public @CallErrorCode int getCode() {
134         return mCode;
135     }
136 
getMessage(String message, int code)137     private static String getMessage(String message, int code) {
138         StringBuilder builder;
139         if (!TextUtils.isEmpty(message)) {
140             builder = new StringBuilder(message);
141             builder.append(" (code: ");
142             builder.append(code);
143             builder.append(")");
144             return builder.toString();
145         } else {
146             return "code: " + code;
147         }
148     }
149 }
150