1 /*
2  * Copyright (C) 2006 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 android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 
22 import com.android.ims.internal.ConferenceParticipant;
23 import com.android.telephony.Rlog;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.stream.Collectors;
28 
29 /**
30  * {@hide}
31  */
32 public abstract class Call {
33     protected final String LOG_TAG = "Call";
34 
35     @UnsupportedAppUsage
Call()36     public Call() {
37     }
38 
39     /* Enums */
40     @UnsupportedAppUsage(implicitMember = "values()[Lcom/android/internal/telephony/Call$State;")
41     public enum State {
42         @UnsupportedAppUsage IDLE,
43         ACTIVE,
44         @UnsupportedAppUsage HOLDING,
45         @UnsupportedAppUsage DIALING,
46         @UnsupportedAppUsage ALERTING,
47         @UnsupportedAppUsage INCOMING,
48         @UnsupportedAppUsage WAITING,
49         @UnsupportedAppUsage DISCONNECTED,
50         @UnsupportedAppUsage DISCONNECTING;
51 
52         @UnsupportedAppUsage
isAlive()53         public boolean isAlive() {
54             return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
55         }
56 
57         @UnsupportedAppUsage
isRinging()58         public boolean isRinging() {
59             return this == INCOMING || this == WAITING;
60         }
61 
isDialing()62         public boolean isDialing() {
63             return this == DIALING || this == ALERTING;
64         }
65     }
66 
67     public static State
stateFromDCState(DriverCall.State dcState)68     stateFromDCState (DriverCall.State dcState) {
69         switch (dcState) {
70             case ACTIVE:        return State.ACTIVE;
71             case HOLDING:       return State.HOLDING;
72             case DIALING:       return State.DIALING;
73             case ALERTING:      return State.ALERTING;
74             case INCOMING:      return State.INCOMING;
75             case WAITING:       return State.WAITING;
76             default:            throw new RuntimeException ("illegal call state:" + dcState);
77         }
78     }
79 
80     public enum SrvccState {
81         NONE, STARTED, COMPLETED, FAILED, CANCELED;
82     }
83 
84     /* Instance Variables */
85 
86     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
87     public State mState = State.IDLE;
88 
89     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
90     public ArrayList<Connection> mConnections = new ArrayList<>();
91 
92     private Object mLock = new Object();
93 
94     /* Instance Methods */
95 
96     /** Do not modify the List result!!! This list is not yours to keep
97      *  It will change across event loop iterations            top
98      */
99 
100     @UnsupportedAppUsage
getConnections()101     public ArrayList<Connection> getConnections() {
102         synchronized (mLock) {
103             return (ArrayList<Connection>) mConnections.clone();
104         }
105     }
106 
107     /**
108      * Get mConnections field from another Call instance.
109      * @param other
110      */
copyConnectionFrom(Call other)111     public void copyConnectionFrom(Call other) {
112         mConnections = other.getConnections();
113     }
114 
115     /**
116      * Get connections count of this instance.
117      * @return the count to return
118      */
getConnectionsCount()119     public int getConnectionsCount() {
120         synchronized (mLock) {
121             return mConnections.size();
122         }
123     }
124 
125     /**
126      * @return returns a summary of the connections held in this call.
127      */
getConnectionSummary()128     public String getConnectionSummary() {
129         synchronized (mLock) {
130             return mConnections.stream()
131                     .map(c -> c.getTelecomCallId() + "/objId:" + System.identityHashCode(c))
132                     .collect(Collectors.joining(", "));
133         }
134     }
135 
136     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getPhone()137     public abstract Phone getPhone();
138     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isMultiparty()139     public abstract boolean isMultiparty();
140     @UnsupportedAppUsage
hangup()141     public abstract void hangup() throws CallStateException;
142 
hangup(@ndroid.telecom.Call.RejectReason int rejectReason)143     public abstract void hangup(@android.telecom.Call.RejectReason int rejectReason)
144             throws CallStateException;
145 
146     /**
147      * hasConnection
148      *
149      * @param c a Connection object
150      * @return true if the call contains the connection object passed in
151      */
hasConnection(Connection c)152     public boolean hasConnection(Connection c) {
153         return c.getCall() == this;
154     }
155 
156     /**
157      * hasConnections
158      * @return true if the call contains one or more connections
159      */
hasConnections()160     public boolean hasConnections() {
161         List<Connection> connections = getConnections();
162 
163         if (connections == null) {
164             return false;
165         }
166 
167         return connections.size() > 0;
168     }
169 
170     /**
171      * removeConnection
172      *
173      * @param conn the connection to be removed
174      */
removeConnection(Connection conn)175     public void removeConnection(Connection conn) {
176         synchronized (mLock) {
177             mConnections.remove(conn);
178         }
179     }
180 
181     /**
182      * addConnection
183      *
184      * @param conn the connection to be added
185      */
addConnection(Connection conn)186     public void addConnection(Connection conn) {
187         synchronized (mLock) {
188             mConnections.add(conn);
189         }
190     }
191 
192     /**
193      * clearConnection
194      */
clearConnections()195     public void clearConnections() {
196         synchronized (mLock) {
197             mConnections.clear();
198         }
199     }
200 
201     /**
202      * getState
203      * @return state of class call
204      */
205     @UnsupportedAppUsage
getState()206     public State getState() {
207         return mState;
208     }
209 
210     /**
211      * getConferenceParticipants
212      * @return List of conference participants.
213      */
getConferenceParticipants()214     public List<ConferenceParticipant> getConferenceParticipants() {
215         return null;
216     }
217 
218     /**
219      * isIdle
220      *
221      * FIXME rename
222      * @return true if the call contains only disconnected connections (if any)
223      */
224     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isIdle()225     public boolean isIdle() {
226         return !getState().isAlive();
227     }
228 
229     /**
230      * Returns the Connection associated with this Call that was created
231      * first, or null if there are no Connections in this Call
232      */
233     @UnsupportedAppUsage
234     public Connection
getEarliestConnection()235     getEarliestConnection() {
236         List<Connection> l;
237         long time = Long.MAX_VALUE;
238         Connection c;
239         Connection earliest = null;
240 
241         l = getConnections();
242 
243         if (l.size() == 0) {
244             return null;
245         }
246 
247         for (int i = 0, s = l.size() ; i < s ; i++) {
248             c = l.get(i);
249             long t;
250 
251             t = c.getCreateTime();
252 
253             if (t < time) {
254                 earliest = c;
255                 time = t;
256             }
257         }
258 
259         return earliest;
260     }
261 
262     public long
getEarliestCreateTime()263     getEarliestCreateTime() {
264         List<Connection> l;
265         long time = Long.MAX_VALUE;
266 
267         l = getConnections();
268 
269         if (l.size() == 0) {
270             return 0;
271         }
272 
273         for (int i = 0, s = l.size() ; i < s ; i++) {
274             Connection c = l.get(i);
275             long t;
276 
277             t = c.getCreateTime();
278 
279             time = t < time ? t : time;
280         }
281 
282         return time;
283     }
284 
285     public long
286     getEarliestConnectTime() {
287         long time = Long.MAX_VALUE;
288         List<Connection> l = getConnections();
289 
290         if (l.size() == 0) {
291             return 0;
292         }
293 
294         for (int i = 0, s = l.size() ; i < s ; i++) {
295             Connection c = l.get(i);
296             long t;
297 
298             t = c.getConnectTime();
299 
300             time = t < time ? t : time;
301         }
302 
303         return time;
304     }
305 
306 
307     public boolean
308     isDialingOrAlerting() {
309         return getState().isDialing();
310     }
311 
312     public boolean
313     isRinging() {
314         return getState().isRinging();
315     }
316 
317     /**
318      * Returns the Connection associated with this Call that was created
319      * last, or null if there are no Connections in this Call
320      */
321     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
322     public Connection
323     getLatestConnection() {
324         List<Connection> l = getConnections();
325         if (l.size() == 0) {
326             return null;
327         }
328 
329         long time = 0;
330         Connection latest = null;
331         for (int i = 0, s = l.size() ; i < s ; i++) {
332             Connection c = l.get(i);
333             long t = c.getCreateTime();
334 
335             if (t > time) {
336                 latest = c;
337                 time = t;
338             }
339         }
340 
341         return latest;
342     }
343 
344     /**
345      * Hangup call if it is alive
346      */
347     public void hangupIfAlive() {
348         if (getState().isAlive()) {
349             try {
350                 hangup();
351             } catch (CallStateException ex) {
352                 Rlog.w(LOG_TAG, " hangupIfActive: caught " + ex);
353             }
354         }
355     }
356 
357     /**
358      * Called when it's time to clean up disconnected Connection objects
359      */
360     public void clearDisconnected() {
361         for (Connection conn : getConnections()) {
362             if (conn.getState() == State.DISCONNECTED) {
363                 removeConnection(conn);
364             }
365         }
366 
367         if (getConnectionsCount() == 0) {
368             setState(State.IDLE);
369         }
370     }
371 
372     protected void setState(State newState) {
373         mState = newState;
374     }
375 }
376