blob: 5192b0f7ccb05706ba4c079543922381c1831a8e [file] [log] [blame]
/*
* Copyright (C) 2014 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 android.telecomm;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import com.android.internal.telecomm.ITelecommService;
import java.util.List;
/**
* Provides access to Telecomm-related functionality.
* TODO: Move this all into PhoneManager.
*/
public class TelecommManager {
/**
* Activity action: Starts the UI for handing an incoming call. This intent starts the in-call
* UI by notifying the Telecomm system that an incoming call exists for a specific call service
* (see {@link android.telecomm.ConnectionService}). Telecomm reads the Intent extras to find
* and bind to the appropriate {@link android.telecomm.ConnectionService} which Telecomm will
* ultimately use to control and get information about the call.
* <p>
* Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
* {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm will then
* ask the connection service for more information about the call prior to showing any UI.
*
* @hide
*/
public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
/**
* The {@link android.content.Intent} action used to configure a
* {@link android.telecomm.ConnectionService}.
*/
public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
"android.intent.action.CONNECTION_SERVICE_CONFIGURE";
/**
* The {@link android.content.Intent} action used to show the call settings page.
*/
public static final String ACTION_SHOW_CALL_SETTINGS =
"android.telecomm.intent.action.SHOW_CALL_SETTINGS";
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
* determines whether the speakerphone should be automatically turned on for an outgoing call.
*/
public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE =
"android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing an integer that
* determines the desired video state for an outgoing call.
* Valid options:
* {@link VideoProfile.VideoState#AUDIO_ONLY},
* {@link VideoProfile.VideoState#BIDIRECTIONAL},
* {@link VideoProfile.VideoState#RX_ENABLED},
* {@link VideoProfile.VideoState#TX_ENABLED}.
*/
public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
"android.intent.extra.START_CALL_WITH_VIDEO_STATE";
/**
* The extra used with an {@link android.content.Intent#ACTION_CALL} and
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a
* {@link PhoneAccountHandle} to use when making the call.
* <p class="note">
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
*/
public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
"android.intent.extra.PHONE_ACCOUNT_HANDLE";
/**
* Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
* metadata about the call. This {@link Bundle} will be returned to the
* {@link ConnectionService}.
*
* @hide
*/
public static final String EXTRA_INCOMING_CALL_EXTRAS =
"android.intent.extra.INCOMING_CALL_EXTRAS";
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} and
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
* which contains metadata about the call. This {@link Bundle} will be saved into
* {@code Call.Details}.
*
* @hide
*/
public static final String EXTRA_OUTGOING_CALL_EXTRAS =
"android.intent.extra.OUTGOING_CALL_EXTRAS";
/**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the disconnect code.
*/
public static final String EXTRA_CALL_DISCONNECT_CAUSE =
"android.telecomm.extra.CALL_DISCONNECT_CAUSE";
/**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the disconnect message.
*/
public static final String EXTRA_CALL_DISCONNECT_MESSAGE =
"android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
/**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the component name of the associated connection service.
*/
public static final String EXTRA_CONNECTION_SERVICE =
"android.telecomm.extra.CONNECTION_SERVICE";
/**
* The number which the party on the other side of the line will see (and use to return the
* call).
* <p>
* {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
* this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
* user's expected caller ID.
*/
public static final String EXTRA_CALL_BACK_NUMBER = "android.telecomm.extra.CALL_BACK_NUMBER";
/**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
public static final char DTMF_CHARACTER_PAUSE = ',';
/**
* The dual-tone multi-frequency signaling character sent to indicate the dialing system should
* wait for user confirmation before proceeding.
*/
public static final char DTMF_CHARACTER_WAIT = ';';
/**
* TTY (teletypewriter) mode is off.
*
* @hide
*/
public static final int TTY_MODE_OFF = 0;
/**
* TTY (teletypewriter) mode is on. The speaker is off and the microphone is muted. The user
* will communicate with the remote party by sending and receiving text messages.
*
* @hide
*/
public static final int TTY_MODE_FULL = 1;
/**
* TTY (teletypewriter) mode is in hearing carryover mode (HCO). The microphone is muted but the
* speaker is on. The user will communicate with the remote party by sending text messages and
* hearing an audible reply.
*
* @hide
*/
public static final int TTY_MODE_HCO = 2;
/**
* TTY (teletypewriter) mode is in voice carryover mode (VCO). The speaker is off but the
* microphone is still on. User will communicate with the remote party by speaking and receiving
* text message replies.
*
* @hide
*/
public static final int TTY_MODE_VCO = 3;
/**
* Broadcast intent action indicating that the current TTY mode has changed. An intent extra
* provides this state as an int.
*
* @see #EXTRA_CURRENT_TTY_MODE
* @hide
*/
public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
"android.telecomm.intent.action.CURRENT_TTY_MODE_CHANGED";
/**
* The lookup key for an int that indicates the current TTY mode.
* Valid modes are:
* - {@link #TTY_MODE_OFF}
* - {@link #TTY_MODE_FULL}
* - {@link #TTY_MODE_HCO}
* - {@link #TTY_MODE_VCO}
*
* @hide
*/
public static final String EXTRA_CURRENT_TTY_MODE =
"android.telecomm.intent.extra.CURRENT_TTY_MODE";
/**
* Broadcast intent action indicating that the TTY preferred operating mode has changed. An
* intent extra provides the new mode as an int.
*
* @see #EXTRA_TTY_PREFERRED_MODE
* @hide
*/
public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
"android.telecomm.intent.action.TTY_PREFERRED_MODE_CHANGED";
/**
* The lookup key for an int that indicates preferred TTY mode. Valid modes are: -
* {@link #TTY_MODE_OFF} - {@link #TTY_MODE_FULL} - {@link #TTY_MODE_HCO} -
* {@link #TTY_MODE_VCO}
*
* @hide
*/
public static final String EXTRA_TTY_PREFERRED_MODE =
"android.telecomm.intent.extra.TTY_PREFERRED";
private static final String TAG = "TelecommManager";
private static final String TELECOMM_SERVICE_NAME = "telecomm";
private final Context mContext;
/**
* @hide
*/
public static TelecommManager from(Context context) {
return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
}
/**
* @hide
*/
public TelecommManager(Context context) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
}
/**
* Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
* calls. This {@code PhoneAccount} will always be a member of the list which is returned from
* calling {@link #getEnabledPhoneAccounts()}.
* <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to initiate a
* phone call must either create their {@link android.content .Intent#ACTION_CALL} or
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
* {@link TelecommManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance to
* select one of the elements of {@link #getEnabledPhoneAccounts()}.
* <p>
* An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
* {@code Intent} with no {@link TelecommManager#EXTRA_PHONE_ACCOUNT_HANDLE} is valid, and
* subsequent steps in the phone call flow are responsible for presenting the user with an
* affordance, if necessary, to choose a {@code PhoneAccount}.
*/
public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
try {
if (isServiceConnected()) {
return getTelecommService().getDefaultOutgoingPhoneAccount();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#getDefaultOutgoingPhoneAccount", e);
}
return null;
}
/**
* Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
* calls.
*
* @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @return A list of {@code PhoneAccountHandle} objects.
*/
public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
try {
if (isServiceConnected()) {
return getTelecommService().getOutgoingPhoneAccounts();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#getOutgoingPhoneAccounts", e);
}
return null;
}
/**
* Determine whether the device has more than one account registered and enabled.
*
* @return {@code true} if the device has more than one account registered and enabled and
* {@code false} otherwise.
*/
public boolean hasMultipleEnabledAccounts() {
return getEnabledPhoneAccounts().size() > 1;
}
/**
* Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
* resources which can be used in a user interface.
*
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
*/
public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
try {
if (isServiceConnected()) {
return getTelecommService().getPhoneAccount(account);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#getPhoneAccount", e);
}
return null;
}
/**
* Register a {@link PhoneAccount} for use by the system.
*
* @param account The complete {@link PhoneAccount}.
*/
public void registerPhoneAccount(PhoneAccount account) {
try {
if (isServiceConnected()) {
getTelecommService().registerPhoneAccount(account);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#registerPhoneAccount", e);
}
}
/**
* Remove a {@link PhoneAccount} registration from the system.
*
* @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
*/
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
getTelecommService().unregisterPhoneAccount(accountHandle);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#unregisterPhoneAccount", e);
}
}
/**
* Remove all Accounts for a given package from the system.
*
* @param packageName A package name that may have registered Accounts.
*/
@SystemApi
public void clearAccounts(String packageName) {
try {
if (isServiceConnected()) {
getTelecommService().clearAccounts(packageName);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#clearAccounts", e);
}
}
/**
* @hide
*/
@SystemApi
public ComponentName getDefaultPhoneApp() {
try {
if (isServiceConnected()) {
return getTelecommService().getDefaultPhoneApp();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
}
return null;
}
/**
* Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
* states).
*
* @hide
*/
@SystemApi
public boolean isInAPhoneCall() {
try {
if (isServiceConnected()) {
return getTelecommService().isInAPhoneCall();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get default phone app.", e);
}
return false;
}
/**
* Returns whether there currently exists is a ringing incoming-call.
*
* @hide
*/
@SystemApi
public boolean isRinging() {
try {
if (isServiceConnected()) {
return getTelecommService().isRinging();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
}
return false;
}
/**
* Ends an ongoing call.
* TODO: L-release - need to convert all invocations of ITelecommService#endCall to use this
* method (clockwork & gearhead).
* @hide
*/
@SystemApi
public boolean endCall() {
try {
if (isServiceConnected()) {
return getTelecommService().endCall();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#endCall", e);
}
return false;
}
/**
* If there is a ringing incoming call, this method accepts the call on behalf of the user.
* TODO: L-release - need to convert all invocation of ITelecommService#answerRingingCall to use
* this method (clockwork & gearhead).
*
* @hide
*/
@SystemApi
public void acceptRingingCall() {
try {
if (isServiceConnected()) {
getTelecommService().acceptRingingCall();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#acceptRingingCall", e);
}
}
/**
* Silences the ringer if a ringing call exists.
*
* @hide
*/
@SystemApi
public void silenceRinger() {
try {
if (isServiceConnected()) {
getTelecommService().silenceRinger();
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
}
}
/**
* Returns whether TTY is supported on this device.
*
* @hide
*/
@SystemApi
public boolean isTtySupported() {
try {
if (isServiceConnected()) {
return getTelecommService().isTtySupported();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
}
return false;
}
/**
* Returns the current TTY mode of the device. For TTY to be on the user must enable it in
* settings and have a wired headset plugged in.
* Valid modes are:
* - {@link TelecommManager#TTY_MODE_OFF}
* - {@link TelecommManager#TTY_MODE_FULL}
* - {@link TelecommManager#TTY_MODE_HCO}
* - {@link TelecommManager#TTY_MODE_VCO}
* @hide
*/
public int getCurrentTtyMode() {
try {
if (isServiceConnected()) {
return getTelecommService().getCurrentTtyMode();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
}
return TTY_MODE_OFF;
}
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
* has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
* with {@link #registerPhoneAccount} and subsequently enabled by the user within the phone's
* settings. Once invoked, this method will cause the system to bind to the
* {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
* additional information about the call (See
* {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
*
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConnection}.
*/
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
getTelecommService().addNewIncomingCall(
phoneAccount, extras == null ? new Bundle() : extras);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
}
}
private ITelecommService getTelecommService() {
return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
}
private boolean isServiceConnected() {
boolean isConnected = getTelecommService() != null;
if (!isConnected) {
Log.w(TAG, "Telecomm Service not found.");
}
return isConnected;
}
}