/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony;
import android.net.Uri;
import android.os.SystemClock;
import android.telecom.ConferenceParticipant;
import android.telephony.Rlog;
import android.util.Log;
import java.lang.Override;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* {@hide}
*/
public abstract class Connection {
public interface PostDialListener {
void onPostDialWait();
void onPostDialChar(char c);
}
/**
* Listener interface for events related to the connection which should be reported to the
* {@link android.telecom.Connection}.
*/
public interface Listener {
public void onVideoStateChanged(int videoState);
public void onLocalVideoCapabilityChanged(boolean capable);
public void onRemoteVideoCapabilityChanged(boolean capable);
public void onWifiChanged(boolean isWifi);
public void onVideoProviderChanged(
android.telecom.Connection.VideoProvider videoProvider);
public void onAudioQualityChanged(int audioQuality);
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
public void onCallSubstateChanged(int callSubstate);
public void onMultipartyStateChanged(boolean isMultiParty);
public void onConferenceMergedFailed();
}
/**
* Base listener implementation.
*/
public abstract static class ListenerBase implements Listener {
@Override
public void onVideoStateChanged(int videoState) {}
@Override
public void onLocalVideoCapabilityChanged(boolean capable) {}
@Override
public void onRemoteVideoCapabilityChanged(boolean capable) {}
@Override
public void onWifiChanged(boolean isWifi) {}
@Override
public void onVideoProviderChanged(
android.telecom.Connection.VideoProvider videoProvider) {}
@Override
public void onAudioQualityChanged(int audioQuality) {}
@Override
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
@Override
public void onCallSubstateChanged(int callSubstate) {}
@Override
public void onMultipartyStateChanged(boolean isMultiParty) {}
@Override
public void onConferenceMergedFailed() {}
}
public static final int AUDIO_QUALITY_STANDARD = 1;
public static final int AUDIO_QUALITY_HIGH_DEFINITION = 2;
//Caller Name Display
protected String mCnapName;
protected int mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
protected String mAddress; // MAY BE NULL!!!
protected String mDialString; // outgoing calls only
protected int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
protected boolean mIsIncoming;
/*
* These time/timespan values are based on System.currentTimeMillis(),
* i.e., "wall clock" time.
*/
protected long mCreateTime;
protected long mConnectTime;
/*
* These time/timespan values are based on SystemClock.elapsedRealTime(),
* i.e., time since boot. They are appropriate for comparison and
* calculating deltas.
*/
protected long mConnectTimeReal;
protected long mDuration;
protected long mHoldingStartTime; // The time when the Connection last transitioned
// into HOLDING
protected Connection mOrigConnection;
private List<PostDialListener> mPostDialListeners = new ArrayList<>();
public Set<Listener> mListeners = new CopyOnWriteArraySet<>();
protected boolean mNumberConverted = false;
protected String mConvertedNumber;
private static String LOG_TAG = "Connection";
Object mUserData;
private int mVideoState;
private boolean mLocalVideoCapable;
private boolean mRemoteVideoCapable;
private boolean mIsWifi;
private int mAudioQuality;
private int mCallSubstate;
private android.telecom.Connection.VideoProvider mVideoProvider;
public Call.State mPreHandoverState = Call.State.IDLE;
/* Instance Methods */
/**
* Gets address (e.g. phone number) associated with connection.
* TODO: distinguish reasons for unavailability
*
* @return address or null if unavailable
*/
public String getAddress() {
return mAddress;
}
/**
* Gets CNAP name associated with connection.
* @return cnap name or null if unavailable
*/
public String getCnapName() {
return mCnapName;
}
/**
* Get original dial string.
* @return original dial string or null if unavailable
*/
public String getOrigDialString(){
return null;
}
/**
* Gets CNAP presentation associated with connection.
* @return cnap name or null if unavailable
*/
public int getCnapNamePresentation() {
return mCnapNamePresentation;
}
/**
* @return Call that owns this Connection, or null if none
*/
public abstract Call getCall();
/**
* Connection create time in currentTimeMillis() format
* Basically, set when object is created.
* Effectively, when an incoming call starts ringing or an
* outgoing call starts dialing
*/
public long getCreateTime() {
return mCreateTime;
}
/**
* Connection connect time in currentTimeMillis() format.
* For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
* For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
* Returns 0 before then.
*/
public long getConnectTime() {
return mConnectTime;
}
/**
* Sets the Connection connect time in currentTimeMillis() format.
*
* @param connectTime the new connect time.
*/
public void setConnectTime(long connectTime) {
mConnectTime = connectTime;
}
/**
* Connection connect time in elapsedRealtime() format.
* For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
* For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
* Returns 0 before then.
*/
public long getConnectTimeReal() {
return mConnectTimeReal;
}
/**
* Disconnect time in currentTimeMillis() format.
* The time when this Connection makes a transition into ENDED or FAIL.
* Returns 0 before then.
*/
public abstract long getDisconnectTime();
/**
* Returns the number of milliseconds the call has been connected,
* or 0 if the call has never connected.
* If the call is still connected, then returns the elapsed
* time since connect.
*/
public long getDurationMillis() {
if (mConnectTimeReal == 0) {
return 0;
} else if (mDuration == 0) {
return SystemClock.elapsedRealtime() - mConnectTimeReal;
} else {
return mDuration;
}
}
/**
* The time when this Connection last transitioned into HOLDING
* in elapsedRealtime() format.
* Returns 0, if it has never made a transition into HOLDING.
*/
public long getHoldingStartTime() {
return mHoldingStartTime;
}
/**
* If this connection is HOLDING, return the number of milliseconds
* that it has been on hold for (approximately).
* If this connection is in any other state, return 0.
*/
public abstract long getHoldDurationMillis();
/**
* Returns call disconnect cause. Values are defined in
* {@link android.telephony.DisconnectCause}. If the call is not yet
* disconnected, NOT_DISCONNECTED is returned.
*/
public abstract int getDisconnectCause();
/**
* Returns a string disconnect cause which is from vendor.
* Vendors may use this string to explain the underline causes of failed calls.
* There is no guarantee that it is non-null nor it'll have meaningful stable values.
* Only use it when getDisconnectCause() returns a value that is not specific enough, like
* ERROR_UNSPECIFIED.
*/
public abstract String getVendorDisconnectCause();
/**
* Returns true of this connection originated elsewhere
* ("MT" or mobile terminated; another party called this terminal)
* or false if this call originated here (MO or mobile originated).
*/
public boolean isIncoming() {
return mIsIncoming;
}
/**
* If this Connection is connected, then it is associated with
* a Call.
*
* Returns getCall().getState() or Call.State.IDLE if not
* connected
*/
public Call.State getState() {
Call c;
c = getCall();
if (c == null) {
return Call.State.IDLE;
} else {
return c.getState();
}
}
/**
* If this connection went through handover return the state of the
* call that contained this connection before handover.
*/
public Call.State getStateBeforeHandover() {
return mPreHandoverState;
}
/**
* Get the details of conference participants. Expected to be
* overwritten by the Connection subclasses.
*/
public List<ConferenceParticipant> getConferenceParticipants() {
Call c;
c = getCall();
if (c == null) {
return null;
} else {
return c.getConferenceParticipants();
}
}
/**
* isAlive()
*
* @return true if the connection isn't disconnected
* (could be active, holding, ringing, dialing, etc)
*/
public boolean
isAlive() {
return getState().isAlive();
}
/**
* Returns true if Connection is connected and is INCOMING or WAITING
*/
public boolean
isRinging() {
return getState().isRinging();
}
/**
*
* @return the userdata set in setUserData()
*/
public Object getUserData() {
return mUserData;
}
/**
*
* @param userdata user can store an any userdata in the Connection object.
*/
public void setUserData(Object userdata) {
mUserData = userdata;
}
/**
* Hangup individual Connection
*/
public abstract void hangup() throws CallStateException;
/**
* Separate this call from its owner Call and assigns it to a new Call
* (eg if it is currently part of a Conference call
* TODO: Throw exception? Does GSM require error display on failure here?
*/
public abstract void separate() throws CallStateException;
public enum PostDialState {
NOT_STARTED, /* The post dial string playback hasn't
been started, or this call is not yet
connected, or this is an incoming call */
STARTED, /* The post dial string playback has begun */
WAIT, /* The post dial string playback is waiting for a
call to proceedAfterWaitChar() */
WILD, /* The post dial string playback is waiting for a
call to proceedAfterWildChar() */
COMPLETE, /* The post dial string playback is complete */
CANCELLED, /* The post dial string playback was cancelled
with cancelPostDial() */
PAUSE /* The post dial string playback is pausing for a
call to processNextPostDialChar*/
}
public void clearUserData(){
mUserData = null;
}
public final void addPostDialListener(PostDialListener listener) {
if (!mPostDialListeners.contains(listener)) {
mPostDialListeners.add(listener);
}
}
public final void removePostDialListener(PostDialListener listener) {
mPostDialListeners.remove(listener);
}
protected final void clearPostDialListeners() {
mPostDialListeners.clear();
}
protected final void notifyPostDialListeners() {
if (getPostDialState() == PostDialState.WAIT) {
for (PostDialListener listener : new ArrayList<>(mPostDialListeners)) {
listener.onPostDialWait();
}
}
}
protected final void notifyPostDialListenersNextChar(char c) {
for (PostDialListener listener : new ArrayList<>(mPostDialListeners)) {
listener.onPostDialChar(c);
}
}
public abstract PostDialState getPostDialState();
/**
* Returns the portion of the post dial string that has not
* yet been dialed, or "" if none
*/
public abstract String getRemainingPostDialString();
/**
* See Phone.setOnPostDialWaitCharacter()
*/
public abstract void proceedAfterWaitChar();
/**
* See Phone.setOnPostDialWildCharacter()
*/
public abstract void proceedAfterWildChar(String str);
/**
* Cancel any post
*/
public abstract void cancelPostDial();
/**
* Returns the caller id presentation type for incoming and waiting calls
* @return one of PRESENTATION_*
*/
public abstract int getNumberPresentation();
/**
* Returns the User to User Signaling (UUS) information associated with
* incoming and waiting calls
* @return UUSInfo containing the UUS userdata.
*/
public abstract UUSInfo getUUSInfo();
/**
* Returns the CallFail reason provided by the RIL with the result of
* RIL_REQUEST_LAST_CALL_FAIL_CAUSE
*/
public abstract int getPreciseDisconnectCause();
/**
* Returns the original Connection instance associated with
* this Connection
*/
public Connection getOrigConnection() {
return mOrigConnection;
}
/**
* Returns whether the original ImsPhoneConnection was a member
* of a conference call
* @return valid only when getOrigConnection() is not null
*/
public abstract boolean isMultiparty();
public void migrateFrom(Connection c) {
if (c == null) return;
mListeners = c.mListeners;
mAddress = c.getAddress();
mNumberPresentation = c.getNumberPresentation();
mDialString = c.getOrigDialString();
mCnapName = c.getCnapName();
mCnapNamePresentation = c.getCnapNamePresentation();
mIsIncoming = c.isIncoming();
mCreateTime = c.getCreateTime();
mConnectTime = c.getConnectTime();
mConnectTimeReal = c.getConnectTimeReal();
mHoldingStartTime = c.getHoldingStartTime();
mOrigConnection = c.getOrigConnection();
}
/**
* Assign a listener to be notified of state changes.
*
* @param listener A listener.
*/
public final void addListener(Listener listener) {
mListeners.add(listener);
}
/**
* Removes a listener.
*
* @param listener A listener.
*/
public final void removeListener(Listener listener) {
mListeners.remove(listener);
}
/**
* Returns the current video state of the connection.
*
* @return The video state of the connection.
*/
public int getVideoState() {
return mVideoState;
}
/**
* Returns the local video capability state for the connection.
*
* @return {@code True} if the connection has local video capabilities.
*/
public boolean isLocalVideoCapable() {
return mLocalVideoCapable;
}
/**
* Returns the remote video capability state for the connection.
*
* @return {@code True} if the connection has remote video capabilities.
*/
public boolean isRemoteVideoCapable() {
return mRemoteVideoCapable;
}
/**
* Returns whether the connection is using a wifi network.
*
* @return {@code True} if the connection is using a wifi network.
*/
public boolean isWifi() {
return mIsWifi;
}
/**
* Returns the {@link android.telecom.Connection.VideoProvider} for the connection.
*
* @return The {@link android.telecom.Connection.VideoProvider}.
*/
public android.telecom.Connection.VideoProvider getVideoProvider() {
return mVideoProvider;
}
/**
* Returns the audio-quality for the connection.
*
* @return The audio quality for the connection.
*/
public int getAudioQuality() {
return mAudioQuality;
}
/**
* Returns the current call substate of the connection.
*
* @return The call substate of the connection.
*/
public int getCallSubstate() {
return mCallSubstate;
}
/**
* Sets the videoState for the current connection and reports the changes to all listeners.
* Valid video states are defined in {@link android.telecom.VideoProfile}.
*
* @return The video state.
*/
public void setVideoState(int videoState) {
mVideoState = videoState;
for (Listener l : mListeners) {
l.onVideoStateChanged(mVideoState);
}
}
/**
* Sets whether video capability is present locally.
*
* @param capable {@code True} if video capable.
*/
public void setLocalVideoCapable(boolean capable) {
mLocalVideoCapable = capable;
for (Listener l : mListeners) {
l.onLocalVideoCapabilityChanged(mLocalVideoCapable);
}
}
/**
* Sets whether video capability is present remotely.
*
* @param capable {@code True} if video capable.
*/
public void setRemoteVideoCapable(boolean capable) {
mRemoteVideoCapable = capable;
for (Listener l : mListeners) {
l.onRemoteVideoCapabilityChanged(mRemoteVideoCapable);
}
}
/**
* Sets whether a wifi network is used for the connection.
*
* @param isWifi {@code True} if wifi is being used.
*/
public void setWifi(boolean isWifi) {
mIsWifi = isWifi;
for (Listener l : mListeners) {
l.onWifiChanged(mIsWifi);
}
}
/**
* Set the audio quality for the connection.
*
* @param audioQuality The audio quality.
*/
public void setAudioQuality(int audioQuality) {
mAudioQuality = audioQuality;
for (Listener l : mListeners) {
l.onAudioQualityChanged(mAudioQuality);
}
}
/**
* Sets the call substate for the current connection and reports the changes to all listeners.
* Valid call substates are defined in {@link android.telecom.Connection}.
*
* @return The call substate.
*/
public void setCallSubstate(int callSubstate) {
mCallSubstate = callSubstate;
for (Listener l : mListeners) {
l.onCallSubstateChanged(mCallSubstate);
}
}
/**
* Sets the {@link android.telecom.Connection.VideoProvider} for the connection.
*
* @param videoProvider The video call provider.
*/
public void setVideoProvider(android.telecom.Connection.VideoProvider videoProvider) {
mVideoProvider = videoProvider;
for (Listener l : mListeners) {
l.onVideoProviderChanged(mVideoProvider);
}
}
public void setConverted(String oriNumber) {
mNumberConverted = true;
mConvertedNumber = mAddress;
mAddress = oriNumber;
mDialString = oriNumber;
}
/**
* Notifies listeners of a change to conference participant(s).
*
* @param conferenceParticipants The participant(s).
*/
public void updateConferenceParticipants(List<ConferenceParticipant> conferenceParticipants) {
for (Listener l : mListeners) {
l.onConferenceParticipantsChanged(conferenceParticipants);
}
}
/**
* Notifies listeners of a change to the multiparty state of the connection.
*
* @param isMultiparty The participant(s).
*/
public void updateMultipartyState(boolean isMultiparty) {
for (Listener l : mListeners) {
l.onMultipartyStateChanged(isMultiparty);
}
}
/**
* Notifies listeners of a failure in merging this connection with the background connection.
*/
public void onConferenceMergeFailed() {
for (Listener l : mListeners) {
l.onConferenceMergedFailed();
}
}
/**
* Notifies this Connection of a request to disconnect a participant of the conference managed
* by the connection.
*
* @param endpoint the {@link Uri} of the participant to disconnect.
*/
public void onDisconnectConferenceParticipant(Uri endpoint) {
}
/**
* Build a human representation of a connection instance, suitable for debugging.
* Don't log personal stuff unless in debug mode.
* @return a string representing the internal state of this connection.
*/
public String toString() {
StringBuilder str = new StringBuilder(128);
if (Rlog.isLoggable(LOG_TAG, Log.DEBUG)) {
str.append("addr: " + getAddress())
.append(" pres.: " + getNumberPresentation())
.append(" dial: " + getOrigDialString())
.append(" postdial: " + getRemainingPostDialString())
.append(" cnap name: " + getCnapName())
.append("(" + getCnapNamePresentation() + ")");
}
str.append(" incoming: " + isIncoming())
.append(" state: " + getState())
.append(" post dial state: " + getPostDialState());
return str.toString();
}
}