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 if (mCallRedirectionAdapter == null) { 104 throw new IllegalStateException("Can only be called from onPlaceCall."); 105 } 106 mCallRedirectionAdapter.placeCallUnmodified(); 107 } catch (RemoteException e) { 108 e.rethrowAsRuntimeException(); 109 } 110 } 111 112 /** 113 * The implemented {@link CallRedirectionService} calls this method to response a request 114 * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that 115 * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing 116 * call. Telecom will cancel the call if the implemented {@link CallRedirectionService} 117 * replies Telecom a handle for an emergency number. 118 * 119 * <p>This can only be called from implemented 120 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the 121 * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. 122 * 123 * @param gatewayUri the gateway uri for call redirection. 124 * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call. 125 * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog 126 * if the confirmFirst is true, and if the redirection request of this 127 * response was sent with a true flag of allowInteractiveResponse via 128 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} 129 */ redirectCall(@onNull Uri gatewayUri, @NonNull PhoneAccountHandle targetPhoneAccount, boolean confirmFirst)130 public final void redirectCall(@NonNull Uri gatewayUri, 131 @NonNull PhoneAccountHandle targetPhoneAccount, 132 boolean confirmFirst) { 133 try { 134 if (mCallRedirectionAdapter == null) { 135 throw new IllegalStateException("Can only be called from onPlaceCall."); 136 } 137 mCallRedirectionAdapter.redirectCall(gatewayUri, targetPhoneAccount, confirmFirst); 138 } catch (RemoteException e) { 139 e.rethrowAsRuntimeException(); 140 } 141 } 142 143 /** 144 * The implemented {@link CallRedirectionService} calls this method to response a request 145 * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that 146 * an outgoing call should be canceled entirely. 147 * 148 * <p>This can only be called from implemented 149 * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the 150 * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. 151 * 152 */ cancelCall()153 public final void cancelCall() { 154 try { 155 if (mCallRedirectionAdapter == null) { 156 throw new IllegalStateException("Can only be called from onPlaceCall."); 157 } 158 mCallRedirectionAdapter.cancelCall(); 159 } catch (RemoteException e) { 160 e.rethrowAsRuntimeException(); 161 } 162 } 163 164 /** 165 * A handler message to process the attempt to place call with redirection service from Telecom 166 */ 167 private static final int MSG_PLACE_CALL = 1; 168 169 /** 170 * A handler to process the attempt to place call with redirection service from Telecom 171 */ 172 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 173 @Override 174 public void handleMessage(Message msg) { 175 switch (msg.what) { 176 case MSG_PLACE_CALL: 177 SomeArgs args = (SomeArgs) msg.obj; 178 try { 179 mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1; 180 onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3, 181 (boolean) args.arg4); 182 } finally { 183 args.recycle(); 184 } 185 break; 186 } 187 } 188 }; 189 190 private final class CallRedirectionBinder extends ICallRedirectionService.Stub { 191 192 /** 193 * Telecom calls this method to inform the CallRedirectionService of a new outgoing call 194 * which is about to be placed. 195 * @param handle the phone number dialed by the user 196 * @param initialPhoneAccount the URI of the number the user dialed 197 * @param allowInteractiveResponse a boolean to tell if the implemented 198 * {@link CallRedirectionService} should allow interactive 199 * responses with users. 200 */ 201 @Override placeCall(@onNull ICallRedirectionAdapter adapter, @NonNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse)202 public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle, 203 @NonNull PhoneAccountHandle initialPhoneAccount, 204 boolean allowInteractiveResponse) { 205 SomeArgs args = SomeArgs.obtain(); 206 args.arg1 = adapter; 207 args.arg2 = handle; 208 args.arg3 = initialPhoneAccount; 209 args.arg4 = allowInteractiveResponse; 210 mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget(); 211 } 212 } 213 214 @Override onBind(@onNull Intent intent)215 public final @Nullable IBinder onBind(@NonNull Intent intent) { 216 return new CallRedirectionBinder(); 217 } 218 219 @Override onUnbind(@onNull Intent intent)220 public final boolean onUnbind(@NonNull Intent intent) { 221 return false; 222 } 223 } 224