1 /* 2 * Copyright (C) 2018 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 android.telecom; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SdkConstant; 22 import android.app.Service; 23 import android.content.Intent; 24 import android.net.Uri; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.os.RemoteException; 30 31 import com.android.internal.os.SomeArgs; 32 import com.android.internal.telecom.ICallRedirectionAdapter; 33 import com.android.internal.telecom.ICallRedirectionService; 34 35 /** 36 * This service can be implemented to interact between Telecom and its implementor 37 * for making outgoing call with optional redirection/cancellation purposes. 38 * 39 * <p> 40 * Below is an example manifest registration for a {@code CallRedirectionService}. 41 * {@code 42 * <service android:name="your.package.YourCallRedirectionServiceImplementation" 43 * android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE"> 44 * <intent-filter> 45 * <action android:name="android.telecom.CallRedirectionService"/> 46 * </intent-filter> 47 * </service> 48 * } 49 */ 50 public abstract class CallRedirectionService extends Service { 51 /** 52 * The {@link Intent} that must be declared as handled by the service. 53 */ 54 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 55 public static final String SERVICE_INTERFACE = "android.telecom.CallRedirectionService"; 56 57 /** 58 * An adapter to inform Telecom the response from the implementor of the Call 59 * Redirection service 60 */ 61 private ICallRedirectionAdapter mCallRedirectionAdapter; 62 63 /** 64 * Telecom calls this method once upon binding to a {@link CallRedirectionService} to inform 65 * it of a new outgoing call which is being placed. Telecom does not request to redirect 66 * emergency calls and does not request to redirect calls with gateway information. 67 * 68 * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from 69 * the implemented {@link CallRedirectionService} set by users. 70 * 71 * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()}, 72 * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only 73 * from here. Calls to these methods are assumed by the Telecom framework to be the response 74 * for the phone call for which {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} was 75 * invoked by Telecom. The Telecom framework will only invoke 76 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} once each time it binds to a 77 * {@link CallRedirectionService}. 78 * 79 * @param handle the phone number dialed by the user, represented in E.164 format if possible 80 * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed. 81 * @param allowInteractiveResponse a boolean to tell if the implemented 82 * {@link CallRedirectionService} should allow interactive 83 * responses with users. Will be {@code false} if, for example 84 * the device is in car mode and the user would not be able to 85 * interact with their device. 86 */ onPlaceCall(@onNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse)87 public abstract void onPlaceCall(@NonNull Uri handle, 88 @NonNull PhoneAccountHandle initialPhoneAccount, 89 boolean allowInteractiveResponse); 90 91 /** 92 * The implemented {@link CallRedirectionService} calls this method to response a request 93 * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that 94 * no changes are required to the outgoing call, and that the call should be placed as-is. 95 * 96 * <p>This can only be called from implemented 97 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the 98 * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. 99 * 100 */ placeCallUnmodified()101 public final void placeCallUnmodified() { 102 try { 103 mCallRedirectionAdapter.placeCallUnmodified(); 104 } catch (RemoteException e) { 105 e.rethrowAsRuntimeException(); 106 } 107 } 108 109 /** 110 * The implemented {@link CallRedirectionService} calls this method to response a request 111 * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that 112 * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing 113 * call. Telecom will cancel the call if the implemented {@link CallRedirectionService} 114 * replies Telecom a handle for an emergency number. 115 * 116 * <p>This can only be called from implemented 117 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the 118 * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. 119 * 120 * @param gatewayUri the gateway uri for call redirection. 121 * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call. 122 * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog 123 * if the confirmFirst is true, and if the redirection request of this 124 * response was sent with a true flag of allowInteractiveResponse via 125 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} 126 */ redirectCall(@onNull Uri gatewayUri, @NonNull PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)127 public final void redirectCall(@NonNull Uri gatewayUri, 128 @NonNull PhoneAccountHandle targetPhoneAccount, 129 boolean confirmFirst) { 130 try { 131 mCallRedirectionAdapter.redirectCall(gatewayUri, targetPhoneAccount, confirmFirst); 132 } catch (RemoteException e) { 133 e.rethrowAsRuntimeException(); 134 } 135 } 136 137 /** 138 * The implemented {@link CallRedirectionService} calls this method to response a request 139 * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that 140 * an outgoing call should be canceled entirely. 141 * 142 * <p>This can only be called from implemented 143 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the 144 * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. 145 * 146 */ cancelCall()147 public final void cancelCall() { 148 try { 149 mCallRedirectionAdapter.cancelCall(); 150 } catch (RemoteException e) { 151 e.rethrowAsRuntimeException(); 152 } 153 } 154 155 /** 156 * A handler message to process the attempt to place call with redirection service from Telecom 157 */ 158 private static final int MSG_PLACE_CALL = 1; 159 160 /** 161 * A handler to process the attempt to place call with redirection service from Telecom 162 */ 163 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 164 @Override 165 public void handleMessage(Message msg) { 166 switch (msg.what) { 167 case MSG_PLACE_CALL: 168 SomeArgs args = (SomeArgs) msg.obj; 169 try { 170 mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1; 171 onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3, 172 (boolean) args.arg4); 173 } finally { 174 args.recycle(); 175 } 176 break; 177 } 178 } 179 }; 180 181 private final class CallRedirectionBinder extends ICallRedirectionService.Stub { 182 183 /** 184 * Telecom calls this method to inform the CallRedirectionService of a new outgoing call 185 * which is about to be placed. 186 * @param handle the phone number dialed by the user 187 * @param initialPhoneAccount the URI of the number the user dialed 188 * @param allowInteractiveResponse a boolean to tell if the implemented 189 * {@link CallRedirectionService} should allow interactive 190 * responses with users. 191 */ 192 @Override placeCall(@onNull ICallRedirectionAdapter adapter, @NonNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse)193 public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle, 194 @NonNull PhoneAccountHandle initialPhoneAccount, 195 boolean allowInteractiveResponse) { 196 SomeArgs args = SomeArgs.obtain(); 197 args.arg1 = adapter; 198 args.arg2 = handle; 199 args.arg3 = initialPhoneAccount; 200 args.arg4 = allowInteractiveResponse; 201 mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget(); 202 } 203 } 204 205 @Override onBind(@onNull Intent intent)206 public final @Nullable IBinder onBind(@NonNull Intent intent) { 207 return new CallRedirectionBinder(); 208 } 209 210 @Override onUnbind(@onNull Intent intent)211 public final boolean onUnbind(@NonNull Intent intent) { 212 return false; 213 } 214 } 215