blob: 36c637723c0a666578ab075f2e530049823c0dab [file] [log] [blame]
/*
* Copyright (C) 2018 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.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallRedirectionAdapter;
import com.android.internal.telecom.ICallRedirectionService;
/**
* This service can be implemented to interact between Telecom and its implementor
* for making outgoing call with optional redirection/cancellation purposes.
*
* <p>
* Below is an example manifest registration for a {@code CallRedirectionService}.
* <pre>
* {@code
* <service android:name="your.package.YourCallRedirectionServiceImplementation"
* android:permission="android.permission.BIND_REDIRECTION_SERVICE">
* <intent-filter>
* <action android:name="android.telecom.CallRedirectionService"/>
* </intent-filter>
* </service>
* }
* </pre>
*/
public abstract class CallRedirectionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
/**
* An adapter to inform Telecom the response from the implementor of the Call
* Redirection service
*/
private ICallRedirectionAdapter mCallRedirectionAdapter;
/**
* Telecom calls this method once upon binding to a {@link CallRedirectionService} to inform
* it of a new outgoing call which is being placed. Telecom does not request to redirect
* emergency calls and does not request to redirect calls with gateway information.
*
* <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from
* the implemented {@link CallRedirectionService} set by users.
*
* <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
* {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only
* from here. Calls to these methods are assumed by the Telecom framework to be the response
* for the phone call for which {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} was
* invoked by Telecom. The Telecom framework will only invoke
* {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} once each time it binds to a
* {@link CallRedirectionService}.
*
* @param handle the phone number dialed by the user, represented in E.164 format if possible
* @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
* @param allowInteractiveResponse a boolean to tell if the implemented
* {@link CallRedirectionService} should allow interactive
* responses with users. Will be {@code false} if, for example
* the device is in car mode and the user would not be able to
* interact with their device.
*/
public abstract void onPlaceCall(@NonNull Uri handle,
@NonNull PhoneAccountHandle initialPhoneAccount,
boolean allowInteractiveResponse);
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
* received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
* no changes are required to the outgoing call, and that the call should be placed as-is.
*
* <p>This can only be called from implemented
* {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the
* latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
*/
public final void placeCallUnmodified() {
try {
mCallRedirectionAdapter.placeCallUnmodified();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
* received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
* changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing
* call. Telecom will cancel the call if the implemented {@link CallRedirectionService}
* replies Telecom a handle for an emergency number.
*
* <p>This can only be called from implemented
* {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the
* latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
* @param gatewayUri the gateway uri for call redirection.
* @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
* @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog
* if the confirmFirst is true, and if the redirection request of this
* response was sent with a true flag of allowInteractiveResponse via
* {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}
*/
public final void redirectCall(@NonNull Uri gatewayUri,
@NonNull PhoneAccountHandle targetPhoneAccount,
boolean confirmFirst) {
try {
mCallRedirectionAdapter.redirectCall(gatewayUri, targetPhoneAccount, confirmFirst);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
* received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
* an outgoing call should be canceled entirely.
*
* <p>This can only be called from implemented
* {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the
* latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
*/
public final void cancelCall() {
try {
mCallRedirectionAdapter.cancelCall();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* A handler message to process the attempt to place call with redirection service from Telecom
*/
private static final int MSG_PLACE_CALL = 1;
/**
* A handler to process the attempt to place call with redirection service from Telecom
*/
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PLACE_CALL:
SomeArgs args = (SomeArgs) msg.obj;
try {
mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3,
(boolean) args.arg4);
} finally {
args.recycle();
}
break;
}
}
};
private final class CallRedirectionBinder extends ICallRedirectionService.Stub {
/**
* Telecom calls this method to inform the CallRedirectionService of a new outgoing call
* which is about to be placed.
* @param handle the phone number dialed by the user
* @param initialPhoneAccount the URI of the number the user dialed
* @param allowInteractiveResponse a boolean to tell if the implemented
* {@link CallRedirectionService} should allow interactive
* responses with users.
*/
@Override
public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle,
@NonNull PhoneAccountHandle initialPhoneAccount,
boolean allowInteractiveResponse) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = adapter;
args.arg2 = handle;
args.arg3 = initialPhoneAccount;
args.arg4 = allowInteractiveResponse;
mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
}
}
@Override
public final @Nullable IBinder onBind(@NonNull Intent intent) {
return new CallRedirectionBinder();
}
@Override
public final boolean onUnbind(@NonNull Intent intent) {
return false;
}
}