1/*
2 * Copyright (C) 2013 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
17package com.android.services.telephony.common;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import com.android.internal.telephony.PhoneConstants;
23import com.google.android.collect.Sets;
24import com.google.common.base.Objects;
25import com.google.common.collect.ImmutableMap;
26import com.google.common.collect.ImmutableSortedSet;
27import com.google.common.primitives.Ints;
28
29import java.util.Map;
30import java.util.SortedSet;
31import java.util.TreeSet;
32
33/**
34 * Class object used across CallHandlerService APIs.
35 * Describes a single call and its state.
36 */
37public final class Call implements Parcelable {
38
39    public static final int INVALID_CALL_ID = -1;
40    public static final int MAX_CONFERENCED_CALLS = 5;
41
42    /* Defines different states of this call */
43    public static class State {
44        public static final int INVALID = 0;
45        public static final int IDLE = 1;           /* The call is idle.  Nothing active */
46        public static final int ACTIVE = 2;         /* There is an active call */
47        public static final int INCOMING = 3;       /* A normal incoming phone call */
48        public static final int CALL_WAITING = 4;   /* Incoming call while another is active */
49        public static final int DIALING = 5;        /* An outgoing call during dial phase */
50        public static final int REDIALING = 6;      /* Subsequent dialing attempt after a failure */
51        public static final int ONHOLD = 7;         /* An active phone call placed on hold */
52        public static final int DISCONNECTING = 8;  /* A call is being ended. */
53        public static final int DISCONNECTED = 9;   /* State after a call disconnects */
54        public static final int CONFERENCED = 10;   /* Call part of a conference call */
55
56        public static boolean isConnected(int state) {
57            switch(state) {
58                case ACTIVE:
59                case INCOMING:
60                case CALL_WAITING:
61                case DIALING:
62                case REDIALING:
63                case ONHOLD:
64                case CONFERENCED:
65                    return true;
66                default:
67            }
68            return false;
69        }
70
71        public static boolean isDialing(int state) {
72            return state == DIALING || state == REDIALING;
73        }
74    }
75
76    /**
77     * Defines a set of capabilities that a call can have as a bit mask.
78     * TODO: Should some of these be capabilities of the Phone instead of the call?
79     * TODO: This is starting to be a mix of capabilities and call properties.  Capabilities
80     *       and properties should be separated.
81     */
82    public static class Capabilities {
83        public static final int HOLD               = 0x00000001; /* has ability to hold the call */
84        public static final int SUPPORT_HOLD       = 0x00000002; /* can show the hold button */
85        public static final int MERGE_CALLS        = 0x00000004; /* has ability to merge calls */
86        public static final int SWAP_CALLS         = 0x00000008; /* swap with a background call */
87        public static final int ADD_CALL           = 0x00000010; /* add another call to this one */
88        public static final int RESPOND_VIA_TEXT   = 0x00000020; /* has respond via text option */
89        public static final int MUTE               = 0x00000040; /* can mute the call */
90        public static final int GENERIC_CONFERENCE = 0x00000080; /* Generic conference mode */
91
92        public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL
93                | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE;
94    }
95
96    /**
97     * Copy of states found in Connection object since Connection object is not available to the UI
98     * code.
99     * TODO: Consider cutting this down to only the types used by the UI.
100     * TODO: Consider adding a CUSTOM cause type and a customDisconnect member variable to
101     *       the Call object.  This would allow OEMs to extend the cause list without
102     *       needing to alter our implementation.
103     */
104    public enum DisconnectCause {
105        NOT_DISCONNECTED,               /* has not yet disconnected */
106        INCOMING_MISSED,                /* an incoming call that was missed and never answered */
107        NORMAL,                         /* normal; remote */
108        LOCAL,                          /* normal; local hangup */
109        BUSY,                           /* outgoing call to busy line */
110        CONGESTION,                     /* outgoing call to congested network */
111        MMI,                            /* not presently used; dial() returns null */
112        INVALID_NUMBER,                 /* invalid dial string */
113        NUMBER_UNREACHABLE,             /* cannot reach the peer */
114        SERVER_UNREACHABLE,             /* cannot reach the server */
115        INVALID_CREDENTIALS,            /* invalid credentials */
116        OUT_OF_NETWORK,                 /* calling from out of network is not allowed */
117        SERVER_ERROR,                   /* server error */
118        TIMED_OUT,                      /* client timed out */
119        LOST_SIGNAL,
120        LIMIT_EXCEEDED,                 /* eg GSM ACM limit exceeded */
121        INCOMING_REJECTED,              /* an incoming call that was rejected */
122        POWER_OFF,                      /* radio is turned off explicitly */
123        OUT_OF_SERVICE,                 /* out of service */
124        ICC_ERROR,                      /* No ICC, ICC locked, or other ICC error */
125        CALL_BARRED,                    /* call was blocked by call barring */
126        FDN_BLOCKED,                    /* call was blocked by fixed dial number */
127        CS_RESTRICTED,                  /* call was blocked by restricted all voice access */
128        CS_RESTRICTED_NORMAL,           /* call was blocked by restricted normal voice access */
129        CS_RESTRICTED_EMERGENCY,        /* call was blocked by restricted emergency voice access */
130        UNOBTAINABLE_NUMBER,            /* Unassigned number (3GPP TS 24.008 table 10.5.123) */
131        CDMA_LOCKED_UNTIL_POWER_CYCLE,  /* MS is locked until next power cycle */
132        CDMA_DROP,
133        CDMA_INTERCEPT,                 /* INTERCEPT order received, MS state idle entered */
134        CDMA_REORDER,                   /* MS has been redirected, call is cancelled */
135        CDMA_SO_REJECT,                 /* service option rejection */
136        CDMA_RETRY_ORDER,               /* requested service is rejected, retry delay is set */
137        CDMA_ACCESS_FAILURE,
138        CDMA_PREEMPTED,
139        CDMA_NOT_EMERGENCY,              /* not an emergency call */
140        CDMA_ACCESS_BLOCKED,            /* Access Blocked by CDMA network */
141        ERROR_UNSPECIFIED,
142
143        UNKNOWN                         /* Disconnect cause doesn't map to any above */
144    }
145
146    private static final Map<Integer, String> STATE_MAP = ImmutableMap.<Integer, String>builder()
147            .put(Call.State.ACTIVE, "ACTIVE")
148            .put(Call.State.CALL_WAITING, "CALL_WAITING")
149            .put(Call.State.DIALING, "DIALING")
150            .put(Call.State.REDIALING, "REDIALING")
151            .put(Call.State.IDLE, "IDLE")
152            .put(Call.State.INCOMING, "INCOMING")
153            .put(Call.State.ONHOLD, "ONHOLD")
154            .put(Call.State.INVALID, "INVALID")
155            .put(Call.State.DISCONNECTING, "DISCONNECTING")
156            .put(Call.State.DISCONNECTED, "DISCONNECTED")
157            .put(Call.State.CONFERENCED, "CONFERENCED")
158            .build();
159
160    // Number presentation type for caller id display
161    // normal
162    public static int PRESENTATION_ALLOWED = PhoneConstants.PRESENTATION_ALLOWED;
163    // block by user
164    public static int PRESENTATION_RESTRICTED = PhoneConstants.PRESENTATION_RESTRICTED;
165    // no specified or unknown by network
166    public static int PRESENTATION_UNKNOWN = PhoneConstants.PRESENTATION_UNKNOWN;
167    // show pay phone info
168    public static int PRESENTATION_PAYPHONE = PhoneConstants.PRESENTATION_PAYPHONE;
169
170    // Unique identifier for the call
171    private int mCallId;
172
173    private CallIdentification mIdentification;
174
175    // The current state of the call
176    private int mState = State.INVALID;
177
178    // Reason for disconnect. Valid when the call state is DISCONNECTED.
179    private DisconnectCause mDisconnectCause = DisconnectCause.UNKNOWN;
180
181    // Bit mask of capabilities unique to this call.
182    private int mCapabilities;
183
184    // Time that this call transitioned into ACTIVE state from INCOMING, WAITING, or OUTGOING.
185    private long mConnectTime = 0;
186
187    // List of call Ids for for this call.  (Used for managing conference calls).
188    private SortedSet<Integer> mChildCallIds = Sets.newSortedSet();
189
190    // Gateway number used to dial this call
191    private String mGatewayNumber;
192
193    // Gateway service package name
194    private String mGatewayPackage;
195
196    public Call(int callId) {
197        mCallId = callId;
198        mIdentification = new CallIdentification(mCallId);
199    }
200
201    public Call(Call call) {
202        mCallId = call.mCallId;
203        mIdentification = new CallIdentification(call.mIdentification);
204        mState = call.mState;
205        mDisconnectCause = call.mDisconnectCause;
206        mCapabilities = call.mCapabilities;
207        mConnectTime = call.mConnectTime;
208        mChildCallIds = new TreeSet<Integer>(call.mChildCallIds);
209        mGatewayNumber = call.mGatewayNumber;
210        mGatewayPackage = call.mGatewayPackage;
211    }
212
213    public int getCallId() {
214        return mCallId;
215    }
216
217    public CallIdentification getIdentification() {
218        return mIdentification;
219    }
220
221    public String getNumber() {
222        return mIdentification.getNumber();
223    }
224
225    public void setNumber(String number) {
226        mIdentification.setNumber(number);
227    }
228
229    public int getState() {
230        return mState;
231    }
232
233    public void setState(int state) {
234        mState = state;
235    }
236
237    public int getNumberPresentation() {
238        return mIdentification.getNumberPresentation();
239    }
240
241    public void setNumberPresentation(int presentation) {
242        mIdentification.setNumberPresentation(presentation);
243    }
244
245    public int getCnapNamePresentation() {
246        return mIdentification.getCnapNamePresentation();
247    }
248
249    public void setCnapNamePresentation(int presentation) {
250        mIdentification.setCnapNamePresentation(presentation);
251    }
252
253    public String getCnapName() {
254        return mIdentification.getCnapName();
255    }
256
257    public void setCnapName(String cnapName) {
258        mIdentification.setCnapName(cnapName);
259    }
260
261    public DisconnectCause getDisconnectCause() {
262        if (mState == State.DISCONNECTED || mState == State.IDLE) {
263            return mDisconnectCause;
264        }
265
266        return DisconnectCause.NOT_DISCONNECTED;
267    }
268
269    public void setDisconnectCause(DisconnectCause cause) {
270        mDisconnectCause = cause;
271    }
272
273    public int getCapabilities() {
274        return mCapabilities;
275    }
276
277    public void setCapabilities(int capabilities) {
278        mCapabilities = (Capabilities.ALL & capabilities);
279    }
280
281    public boolean can(int capabilities) {
282        return (capabilities == (capabilities & mCapabilities));
283    }
284
285    public void addCapabilities(int capabilities) {
286        setCapabilities(capabilities | mCapabilities);
287    }
288
289    public void setConnectTime(long connectTime) {
290        mConnectTime = connectTime;
291    }
292
293    public long getConnectTime() {
294        return mConnectTime;
295    }
296
297    public void removeChildId(int id) {
298        mChildCallIds.remove(id);
299    }
300
301    public void removeAllChildren() {
302        mChildCallIds.clear();
303    }
304
305    public void addChildId(int id) {
306        mChildCallIds.add(id);
307    }
308
309    public ImmutableSortedSet<Integer> getChildCallIds() {
310        return ImmutableSortedSet.copyOf(mChildCallIds);
311    }
312
313    public boolean isConferenceCall() {
314        return mChildCallIds.size() >= 2;
315    }
316
317    public String getGatewayNumber() {
318        return mGatewayNumber;
319    }
320
321    public void setGatewayNumber(String number) {
322        mGatewayNumber = number;
323    }
324
325    public String getGatewayPackage() {
326        return mGatewayPackage;
327    }
328
329    public void setGatewayPackage(String packageName) {
330        mGatewayPackage = packageName;
331    }
332
333    /**
334     * Parcelable implementation
335     */
336
337    @Override
338    public void writeToParcel(Parcel dest, int flags) {
339        dest.writeInt(mCallId);
340        dest.writeInt(mState);
341        dest.writeString(getDisconnectCause().toString());
342        dest.writeInt(getCapabilities());
343        dest.writeLong(getConnectTime());
344        dest.writeIntArray(Ints.toArray(mChildCallIds));
345        dest.writeString(getGatewayNumber());
346        dest.writeString(getGatewayPackage());
347        dest.writeParcelable(mIdentification, 0);
348    }
349
350    /**
351     * Constructor for Parcelable implementation.
352     */
353    private Call(Parcel in) {
354        mCallId = in.readInt();
355        mState = in.readInt();
356        mDisconnectCause = DisconnectCause.valueOf(in.readString());
357        mCapabilities = in.readInt();
358        mConnectTime = in.readLong();
359        mChildCallIds.addAll(Ints.asList(in.createIntArray()));
360        mGatewayNumber = in.readString();
361        mGatewayPackage = in.readString();
362        mIdentification = in.readParcelable(CallIdentification.class.getClassLoader());
363    }
364
365    @Override
366    public int describeContents() {
367        return 0;
368    }
369
370    /**
371     * Creates Call objects for Parcelable implementation.
372     */
373    public static final Parcelable.Creator<Call> CREATOR
374            = new Parcelable.Creator<Call>() {
375
376        @Override
377        public Call createFromParcel(Parcel in) {
378            return new Call(in);
379        }
380
381        @Override
382        public Call[] newArray(int size) {
383            return new Call[size];
384        }
385    };
386
387    @Override
388    public String toString() {
389        return Objects.toStringHelper(this)
390                .add("mCallId", mCallId)
391                .add("mState", STATE_MAP.get(mState))
392                .add("mDisconnectCause", mDisconnectCause)
393                .add("mCapabilities", mCapabilities)
394                .add("mConnectTime", mConnectTime)
395                .add("mChildCallIds", mChildCallIds)
396                .add("mGatewayNumber", MoreStrings.toSafeString(mGatewayNumber))
397                .add("mGatewayPackage", mGatewayPackage)
398                .add("mIdentification", mIdentification)
399                .toString();
400    }
401}
402