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
17package com.android.internal.telephony;
18
19import android.net.Uri;
20import android.os.Bundle;
21import android.os.SystemClock;
22import android.telecom.ConferenceParticipant;
23import android.telephony.Rlog;
24import android.util.Log;
25
26import java.lang.Override;
27import java.util.ArrayList;
28import java.util.List;
29import java.util.Set;
30import java.util.concurrent.CopyOnWriteArraySet;
31
32/**
33 * {@hide}
34 */
35public abstract class Connection {
36    public interface PostDialListener {
37        void onPostDialWait();
38        void onPostDialChar(char c);
39    }
40
41    /**
42     * Listener interface for events related to the connection which should be reported to the
43     * {@link android.telecom.Connection}.
44     */
45    public interface Listener {
46        public void onVideoStateChanged(int videoState);
47        public void onLocalVideoCapabilityChanged(boolean capable);
48        public void onRemoteVideoCapabilityChanged(boolean capable);
49        public void onWifiChanged(boolean isWifi);
50        public void onVideoProviderChanged(
51                android.telecom.Connection.VideoProvider videoProvider);
52        public void onAudioQualityChanged(int audioQuality);
53        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
54        public void onCallSubstateChanged(int callSubstate);
55        public void onMultipartyStateChanged(boolean isMultiParty);
56        public void onConferenceMergedFailed();
57        public void onExtrasChanged(Bundle extras);
58    }
59
60    /**
61     * Base listener implementation.
62     */
63    public abstract static class ListenerBase implements Listener {
64        @Override
65        public void onVideoStateChanged(int videoState) {}
66        @Override
67        public void onLocalVideoCapabilityChanged(boolean capable) {}
68        @Override
69        public void onRemoteVideoCapabilityChanged(boolean capable) {}
70        @Override
71        public void onWifiChanged(boolean isWifi) {}
72        @Override
73        public void onVideoProviderChanged(
74                android.telecom.Connection.VideoProvider videoProvider) {}
75        @Override
76        public void onAudioQualityChanged(int audioQuality) {}
77        @Override
78        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
79        @Override
80        public void onCallSubstateChanged(int callSubstate) {}
81        @Override
82        public void onMultipartyStateChanged(boolean isMultiParty) {}
83        @Override
84        public void onConferenceMergedFailed() {}
85        @Override
86        public void onExtrasChanged(Bundle extras) {}
87    }
88
89    public static final int AUDIO_QUALITY_STANDARD = 1;
90    public static final int AUDIO_QUALITY_HIGH_DEFINITION = 2;
91
92    //Caller Name Display
93    protected String mCnapName;
94    protected int mCnapNamePresentation  = PhoneConstants.PRESENTATION_ALLOWED;
95    protected String mAddress;     // MAY BE NULL!!!
96    protected String mDialString;          // outgoing calls only
97    protected int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
98    protected boolean mIsIncoming;
99    /*
100     * These time/timespan values are based on System.currentTimeMillis(),
101     * i.e., "wall clock" time.
102     */
103    protected long mCreateTime;
104    protected long mConnectTime;
105    /*
106     * These time/timespan values are based on SystemClock.elapsedRealTime(),
107     * i.e., time since boot.  They are appropriate for comparison and
108     * calculating deltas.
109     */
110    protected long mConnectTimeReal;
111    protected long mDuration;
112    protected long mHoldingStartTime;  // The time when the Connection last transitioned
113                            // into HOLDING
114    protected Connection mOrigConnection;
115    private List<PostDialListener> mPostDialListeners = new ArrayList<>();
116    public Set<Listener> mListeners = new CopyOnWriteArraySet<>();
117
118    protected boolean mNumberConverted = false;
119    protected String mConvertedNumber;
120
121    private static String LOG_TAG = "Connection";
122
123    Object mUserData;
124    private int mVideoState;
125    private boolean mLocalVideoCapable;
126    private boolean mRemoteVideoCapable;
127    private boolean mIsWifi;
128    private int mAudioQuality;
129    private int mCallSubstate;
130    private android.telecom.Connection.VideoProvider mVideoProvider;
131    public Call.State mPreHandoverState = Call.State.IDLE;
132    private Bundle mExtras;
133
134    /* Instance Methods */
135
136    /**
137     * Gets address (e.g. phone number) associated with connection.
138     * TODO: distinguish reasons for unavailability
139     *
140     * @return address or null if unavailable
141     */
142
143    public String getAddress() {
144        return mAddress;
145    }
146
147    /**
148     * Gets CNAP name associated with connection.
149     * @return cnap name or null if unavailable
150     */
151    public String getCnapName() {
152        return mCnapName;
153    }
154
155    /**
156     * Get original dial string.
157     * @return original dial string or null if unavailable
158     */
159    public String getOrigDialString(){
160        return null;
161    }
162
163    /**
164     * Gets CNAP presentation associated with connection.
165     * @return cnap name or null if unavailable
166     */
167
168    public int getCnapNamePresentation() {
169       return mCnapNamePresentation;
170    }
171
172    /**
173     * @return Call that owns this Connection, or null if none
174     */
175    public abstract Call getCall();
176
177    /**
178     * Connection create time in currentTimeMillis() format
179     * Basically, set when object is created.
180     * Effectively, when an incoming call starts ringing or an
181     * outgoing call starts dialing
182     */
183    public long getCreateTime() {
184        return mCreateTime;
185    }
186
187    /**
188     * Connection connect time in currentTimeMillis() format.
189     * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
190     * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
191     * Returns 0 before then.
192     */
193    public long getConnectTime() {
194        return mConnectTime;
195    }
196
197    /**
198     * Sets the Connection connect time in currentTimeMillis() format.
199     *
200     * @param connectTime the new connect time.
201     */
202    public void setConnectTime(long connectTime) {
203        mConnectTime = connectTime;
204    }
205
206    /**
207     * Connection connect time in elapsedRealtime() format.
208     * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
209     * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
210     * Returns 0 before then.
211     */
212    public long getConnectTimeReal() {
213        return mConnectTimeReal;
214    }
215
216    /**
217     * Disconnect time in currentTimeMillis() format.
218     * The time when this Connection makes a transition into ENDED or FAIL.
219     * Returns 0 before then.
220     */
221    public abstract long getDisconnectTime();
222
223    /**
224     * Returns the number of milliseconds the call has been connected,
225     * or 0 if the call has never connected.
226     * If the call is still connected, then returns the elapsed
227     * time since connect.
228     */
229    public long getDurationMillis() {
230        if (mConnectTimeReal == 0) {
231            return 0;
232        } else if (mDuration == 0) {
233            return SystemClock.elapsedRealtime() - mConnectTimeReal;
234        } else {
235            return mDuration;
236        }
237    }
238
239    /**
240     * The time when this Connection last transitioned into HOLDING
241     * in elapsedRealtime() format.
242     * Returns 0, if it has never made a transition into HOLDING.
243     */
244    public long getHoldingStartTime() {
245        return mHoldingStartTime;
246    }
247
248    /**
249     * If this connection is HOLDING, return the number of milliseconds
250     * that it has been on hold for (approximately).
251     * If this connection is in any other state, return 0.
252     */
253
254    public abstract long getHoldDurationMillis();
255
256    /**
257     * Returns call disconnect cause. Values are defined in
258     * {@link android.telephony.DisconnectCause}. If the call is not yet
259     * disconnected, NOT_DISCONNECTED is returned.
260     */
261    public abstract int getDisconnectCause();
262
263    /**
264     * Returns a string disconnect cause which is from vendor.
265     * Vendors may use this string to explain the underline causes of failed calls.
266     * There is no guarantee that it is non-null nor it'll have meaningful stable values.
267     * Only use it when getDisconnectCause() returns a value that is not specific enough, like
268     * ERROR_UNSPECIFIED.
269     */
270    public abstract String getVendorDisconnectCause();
271
272    /**
273     * Returns true of this connection originated elsewhere
274     * ("MT" or mobile terminated; another party called this terminal)
275     * or false if this call originated here (MO or mobile originated).
276     */
277    public boolean isIncoming() {
278        return mIsIncoming;
279    }
280
281    /**
282     * If this Connection is connected, then it is associated with
283     * a Call.
284     *
285     * Returns getCall().getState() or Call.State.IDLE if not
286     * connected
287     */
288    public Call.State getState() {
289        Call c;
290
291        c = getCall();
292
293        if (c == null) {
294            return Call.State.IDLE;
295        } else {
296            return c.getState();
297        }
298    }
299
300    /**
301     * If this connection went through handover return the state of the
302     * call that contained this connection before handover.
303     */
304    public Call.State getStateBeforeHandover() {
305        return mPreHandoverState;
306   }
307
308    /**
309     * Get the details of conference participants. Expected to be
310     * overwritten by the Connection subclasses.
311     */
312    public List<ConferenceParticipant> getConferenceParticipants() {
313        Call c;
314
315        c = getCall();
316
317        if (c == null) {
318            return null;
319        } else {
320            return c.getConferenceParticipants();
321        }
322    }
323
324    /**
325     * isAlive()
326     *
327     * @return true if the connection isn't disconnected
328     * (could be active, holding, ringing, dialing, etc)
329     */
330    public boolean
331    isAlive() {
332        return getState().isAlive();
333    }
334
335    /**
336     * Returns true if Connection is connected and is INCOMING or WAITING
337     */
338    public boolean
339    isRinging() {
340        return getState().isRinging();
341    }
342
343    /**
344     *
345     * @return the userdata set in setUserData()
346     */
347    public Object getUserData() {
348        return mUserData;
349    }
350
351    /**
352     *
353     * @param userdata user can store an any userdata in the Connection object.
354     */
355    public void setUserData(Object userdata) {
356        mUserData = userdata;
357    }
358
359    /**
360     * Hangup individual Connection
361     */
362    public abstract void hangup() throws CallStateException;
363
364    /**
365     * Separate this call from its owner Call and assigns it to a new Call
366     * (eg if it is currently part of a Conference call
367     * TODO: Throw exception? Does GSM require error display on failure here?
368     */
369    public abstract void separate() throws CallStateException;
370
371    public enum PostDialState {
372        NOT_STARTED,    /* The post dial string playback hasn't
373                           been started, or this call is not yet
374                           connected, or this is an incoming call */
375        STARTED,        /* The post dial string playback has begun */
376        WAIT,           /* The post dial string playback is waiting for a
377                           call to proceedAfterWaitChar() */
378        WILD,           /* The post dial string playback is waiting for a
379                           call to proceedAfterWildChar() */
380        COMPLETE,       /* The post dial string playback is complete */
381        CANCELLED,       /* The post dial string playback was cancelled
382                           with cancelPostDial() */
383        PAUSE           /* The post dial string playback is pausing for a
384                           call to processNextPostDialChar*/
385    }
386
387    public void clearUserData(){
388        mUserData = null;
389    }
390
391    public final void addPostDialListener(PostDialListener listener) {
392        if (!mPostDialListeners.contains(listener)) {
393            mPostDialListeners.add(listener);
394        }
395    }
396
397    public final void removePostDialListener(PostDialListener listener) {
398        mPostDialListeners.remove(listener);
399    }
400
401    protected final void clearPostDialListeners() {
402        mPostDialListeners.clear();
403    }
404
405    protected final void notifyPostDialListeners() {
406        if (getPostDialState() == PostDialState.WAIT) {
407            for (PostDialListener listener : new ArrayList<>(mPostDialListeners)) {
408                listener.onPostDialWait();
409            }
410        }
411    }
412
413    protected final void notifyPostDialListenersNextChar(char c) {
414        for (PostDialListener listener : new ArrayList<>(mPostDialListeners)) {
415            listener.onPostDialChar(c);
416        }
417    }
418
419    public abstract PostDialState getPostDialState();
420
421    /**
422     * Returns the portion of the post dial string that has not
423     * yet been dialed, or "" if none
424     */
425    public abstract String getRemainingPostDialString();
426
427    /**
428     * See Phone.setOnPostDialWaitCharacter()
429     */
430
431    public abstract void proceedAfterWaitChar();
432
433    /**
434     * See Phone.setOnPostDialWildCharacter()
435     */
436    public abstract void proceedAfterWildChar(String str);
437    /**
438     * Cancel any post
439     */
440    public abstract void cancelPostDial();
441
442    /**
443     * Returns the caller id presentation type for incoming and waiting calls
444     * @return one of PRESENTATION_*
445     */
446    public abstract int getNumberPresentation();
447
448    /**
449     * Returns the User to User Signaling (UUS) information associated with
450     * incoming and waiting calls
451     * @return UUSInfo containing the UUS userdata.
452     */
453    public abstract UUSInfo getUUSInfo();
454
455    /**
456     * Returns the CallFail reason provided by the RIL with the result of
457     * RIL_REQUEST_LAST_CALL_FAIL_CAUSE
458     */
459    public abstract int getPreciseDisconnectCause();
460
461    /**
462     * Returns the original Connection instance associated with
463     * this Connection
464     */
465    public Connection getOrigConnection() {
466        return mOrigConnection;
467    }
468
469    /**
470     * Returns whether the original ImsPhoneConnection was a member
471     * of a conference call
472     * @return valid only when getOrigConnection() is not null
473     */
474    public abstract boolean isMultiparty();
475
476    public void migrateFrom(Connection c) {
477        if (c == null) return;
478        mListeners = c.mListeners;
479        mDialString = c.getOrigDialString();
480        mCreateTime = c.getCreateTime();
481        mConnectTime = c.getConnectTime();
482        mConnectTimeReal = c.getConnectTimeReal();
483        mHoldingStartTime = c.getHoldingStartTime();
484        mOrigConnection = c.getOrigConnection();
485    }
486
487    /**
488     * Assign a listener to be notified of state changes.
489     *
490     * @param listener A listener.
491     */
492    public final void addListener(Listener listener) {
493        mListeners.add(listener);
494    }
495
496    /**
497     * Removes a listener.
498     *
499     * @param listener A listener.
500     */
501    public final void removeListener(Listener listener) {
502        mListeners.remove(listener);
503    }
504
505    /**
506     * Returns the current video state of the connection.
507     *
508     * @return The video state of the connection.
509     */
510    public int getVideoState() {
511        return mVideoState;
512    }
513
514    /**
515     * Returns the local video capability state for the connection.
516     *
517     * @return {@code True} if the connection has local video capabilities.
518     */
519    public boolean isLocalVideoCapable() {
520        return mLocalVideoCapable;
521    }
522
523    /**
524     * Returns the remote video capability state for the connection.
525     *
526     * @return {@code True} if the connection has remote video capabilities.
527     */
528    public boolean isRemoteVideoCapable() {
529        return mRemoteVideoCapable;
530    }
531
532    /**
533     * Returns whether the connection is using a wifi network.
534     *
535     * @return {@code True} if the connection is using a wifi network.
536     */
537    public boolean isWifi() {
538        return mIsWifi;
539    }
540
541    /**
542     * Returns the {@link android.telecom.Connection.VideoProvider} for the connection.
543     *
544     * @return The {@link android.telecom.Connection.VideoProvider}.
545     */
546    public android.telecom.Connection.VideoProvider getVideoProvider() {
547        return mVideoProvider;
548    }
549
550    /**
551     * Returns the audio-quality for the connection.
552     *
553     * @return The audio quality for the connection.
554     */
555    public int getAudioQuality() {
556        return mAudioQuality;
557    }
558
559
560    /**
561     * Returns the current call substate of the connection.
562     *
563     * @return The call substate of the connection.
564     */
565    public int getCallSubstate() {
566        return mCallSubstate;
567    }
568
569
570    /**
571     * Sets the videoState for the current connection and reports the changes to all listeners.
572     * Valid video states are defined in {@link android.telecom.VideoProfile}.
573     *
574     * @return The video state.
575     */
576    public void setVideoState(int videoState) {
577        mVideoState = videoState;
578        for (Listener l : mListeners) {
579            l.onVideoStateChanged(mVideoState);
580        }
581    }
582
583    /**
584     * Sets whether video capability is present locally.
585     *
586     * @param capable {@code True} if video capable.
587     */
588    public void setLocalVideoCapable(boolean capable) {
589        mLocalVideoCapable = capable;
590        for (Listener l : mListeners) {
591            l.onLocalVideoCapabilityChanged(mLocalVideoCapable);
592        }
593    }
594
595    /**
596     * Sets whether video capability is present remotely.
597     *
598     * @param capable {@code True} if video capable.
599     */
600    public void setRemoteVideoCapable(boolean capable) {
601        mRemoteVideoCapable = capable;
602        for (Listener l : mListeners) {
603            l.onRemoteVideoCapabilityChanged(mRemoteVideoCapable);
604        }
605    }
606
607    /**
608     * Sets whether a wifi network is used for the connection.
609     *
610     * @param isWifi {@code True} if wifi is being used.
611     */
612    public void setWifi(boolean isWifi) {
613        mIsWifi = isWifi;
614        for (Listener l : mListeners) {
615            l.onWifiChanged(mIsWifi);
616        }
617    }
618
619    /**
620     * Set the audio quality for the connection.
621     *
622     * @param audioQuality The audio quality.
623     */
624    public void setAudioQuality(int audioQuality) {
625        mAudioQuality = audioQuality;
626        for (Listener l : mListeners) {
627            l.onAudioQualityChanged(mAudioQuality);
628        }
629    }
630
631    /**
632     * Notifies listeners that connection extras has changed.
633     * @param extras New connection extras.
634     */
635    public void setConnectionExtras(Bundle extras) {
636        mExtras = extras;
637        for (Listener l : mListeners) {
638            l.onExtrasChanged(extras);
639        }
640    }
641
642    /**
643     * Retrieves the current connection extras.
644     * @return the connection extras.
645     */
646    public Bundle getConnectionExtras() {
647        return mExtras;
648    }
649
650    /**
651     * Sets the call substate for the current connection and reports the changes to all listeners.
652     * Valid call substates are defined in {@link android.telecom.Connection}.
653     *
654     * @return The call substate.
655     */
656    public void setCallSubstate(int callSubstate) {
657        mCallSubstate = callSubstate;
658        for (Listener l : mListeners) {
659            l.onCallSubstateChanged(mCallSubstate);
660        }
661    }
662
663    /**
664     * Sets the {@link android.telecom.Connection.VideoProvider} for the connection.
665     *
666     * @param videoProvider The video call provider.
667     */
668    public void setVideoProvider(android.telecom.Connection.VideoProvider videoProvider) {
669        mVideoProvider = videoProvider;
670        for (Listener l : mListeners) {
671            l.onVideoProviderChanged(mVideoProvider);
672        }
673    }
674
675    public void setConverted(String oriNumber) {
676        mNumberConverted = true;
677        mConvertedNumber = mAddress;
678        mAddress = oriNumber;
679        mDialString = oriNumber;
680    }
681
682    /**
683     * Notifies listeners of a change to conference participant(s).
684     *
685     * @param conferenceParticipants The participant(s).
686     */
687    public void updateConferenceParticipants(List<ConferenceParticipant> conferenceParticipants) {
688        for (Listener l : mListeners) {
689            l.onConferenceParticipantsChanged(conferenceParticipants);
690        }
691    }
692
693    /**
694     * Notifies listeners of a change to the multiparty state of the connection.
695     *
696     * @param isMultiparty The participant(s).
697     */
698    public void updateMultipartyState(boolean isMultiparty) {
699        for (Listener l : mListeners) {
700            l.onMultipartyStateChanged(isMultiparty);
701        }
702    }
703
704    /**
705     * Notifies listeners of a failure in merging this connection with the background connection.
706     */
707    public void onConferenceMergeFailed() {
708        for (Listener l : mListeners) {
709            l.onConferenceMergedFailed();
710        }
711    }
712
713    /**
714     * Notifies this Connection of a request to disconnect a participant of the conference managed
715     * by the connection.
716     *
717     * @param endpoint the {@link Uri} of the participant to disconnect.
718     */
719    public void onDisconnectConferenceParticipant(Uri endpoint) {
720    }
721
722    /**
723     * Build a human representation of a connection instance, suitable for debugging.
724     * Don't log personal stuff unless in debug mode.
725     * @return a string representing the internal state of this connection.
726     */
727    public String toString() {
728        StringBuilder str = new StringBuilder(128);
729
730        if (Rlog.isLoggable(LOG_TAG, Log.DEBUG)) {
731            str.append("addr: " + getAddress())
732                    .append(" pres.: " + getNumberPresentation())
733                    .append(" dial: " + getOrigDialString())
734                    .append(" postdial: " + getRemainingPostDialString())
735                    .append(" cnap name: " + getCnapName())
736                    .append("(" + getCnapNamePresentation() + ")");
737        }
738        str.append(" incoming: " + isIncoming())
739                .append(" state: " + getState())
740                .append(" post dial state: " + getPostDialState());
741        return str.toString();
742    }
743}
744