1 /* 2 * Copyright (C) 2016 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.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SdkConstant; 23 import android.annotation.SystemApi; 24 import android.annotation.TestApi; 25 import android.app.Service; 26 import android.content.ComponentName; 27 import android.content.Intent; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.RemoteException; 34 35 import com.android.internal.os.SomeArgs; 36 import com.android.internal.telecom.ICallScreeningAdapter; 37 import com.android.internal.telecom.ICallScreeningService; 38 39 /** 40 * This service can be implemented by the default dialer (see 41 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow 42 * incoming calls before they are shown to a user. A {@link CallScreeningService} can also see 43 * outgoing calls for the purpose of providing caller ID services for those calls. 44 * <p> 45 * Below is an example manifest registration for a {@code CallScreeningService}. 46 * <pre> 47 * {@code 48 * <service android:name="your.package.YourCallScreeningServiceImplementation" 49 * android:permission="android.permission.BIND_SCREENING_SERVICE"> 50 * <intent-filter> 51 * <action android:name="android.telecom.CallScreeningService"/> 52 * </intent-filter> 53 * </service> 54 * } 55 * </pre> 56 * <p> 57 * A CallScreeningService performs two functions: 58 * <ol> 59 * <li>Call blocking/screening - the service can choose which calls will ring on the user's 60 * device, and which will be silently sent to voicemail.</li> 61 * <li>Call identification - services which provide call identification functionality can 62 * display a user-interface of their choosing which contains identifying information for a call. 63 * </li> 64 * </ol> 65 * <p> 66 * <h2>Becoming the {@link CallScreeningService}</h2> 67 * Telecom will bind to a single app chosen by the user which implements the 68 * {@link CallScreeningService} API when there are new incoming and outgoing calls. 69 * <p> 70 * The code snippet below illustrates how your app can request that it fills the call screening 71 * role. 72 * <pre> 73 * {@code 74 * private static final int REQUEST_ID = 1; 75 * 76 * public void requestRole() { 77 * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE); 78 * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING); 79 * startActivityForResult(intent, REQUEST_ID); 80 * } 81 * 82 * @Override 83 * public void onActivityResult(int requestCode, int resultCode, Intent data) { 84 * if (requestCode == REQUEST_ID) { 85 * if (resultCode == android.app.Activity.RESULT_OK) { 86 * // Your app is now the call screening app 87 * } else { 88 * // Your app is not the call screening app 89 * } 90 * } 91 * } 92 * </pre> 93 */ 94 public abstract class CallScreeningService extends Service { 95 /** 96 * The {@link Intent} that must be declared as handled by the service. 97 */ 98 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 99 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService"; 100 101 private static final int MSG_SCREEN_CALL = 1; 102 103 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 104 @Override 105 public void handleMessage(Message msg) { 106 switch (msg.what) { 107 case MSG_SCREEN_CALL: 108 SomeArgs args = (SomeArgs) msg.obj; 109 try { 110 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1; 111 Call.Details callDetails = Call.Details 112 .createFromParcelableCall((ParcelableCall) args.arg2); 113 onScreenCall(callDetails); 114 if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) { 115 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); 116 } 117 } catch (RemoteException e) { 118 Log.w(this, "Exception when screening call: " + e); 119 } finally { 120 args.recycle(); 121 } 122 break; 123 } 124 } 125 }; 126 127 private final class CallScreeningBinder extends ICallScreeningService.Stub { 128 @Override screenCall(ICallScreeningAdapter adapter, ParcelableCall call)129 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) { 130 Log.v(this, "screenCall"); 131 SomeArgs args = SomeArgs.obtain(); 132 args.arg1 = adapter; 133 args.arg2 = call; 134 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget(); 135 } 136 } 137 138 private ICallScreeningAdapter mCallScreeningAdapter; 139 140 /* 141 * Information about how to respond to an incoming call. 142 */ 143 public static class CallResponse { 144 private final boolean mShouldDisallowCall; 145 private final boolean mShouldRejectCall; 146 private final boolean mShouldSilenceCall; 147 private final boolean mShouldSkipCallLog; 148 private final boolean mShouldSkipNotification; 149 private final boolean mShouldScreenCallViaAudioProcessing; 150 CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification, boolean shouldScreenCallViaAudioProcessing)151 private CallResponse( 152 boolean shouldDisallowCall, 153 boolean shouldRejectCall, 154 boolean shouldSilenceCall, 155 boolean shouldSkipCallLog, 156 boolean shouldSkipNotification, 157 boolean shouldScreenCallViaAudioProcessing) { 158 if (!shouldDisallowCall 159 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) { 160 throw new IllegalStateException("Invalid response state for allowed call."); 161 } 162 163 if (shouldDisallowCall && shouldScreenCallViaAudioProcessing) { 164 throw new IllegalStateException("Invalid response state for allowed call."); 165 } 166 167 mShouldDisallowCall = shouldDisallowCall; 168 mShouldRejectCall = shouldRejectCall; 169 mShouldSkipCallLog = shouldSkipCallLog; 170 mShouldSkipNotification = shouldSkipNotification; 171 mShouldSilenceCall = shouldSilenceCall; 172 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing; 173 } 174 175 /* 176 * @return Whether the incoming call should be blocked. 177 */ getDisallowCall()178 public boolean getDisallowCall() { 179 return mShouldDisallowCall; 180 } 181 182 /* 183 * @return Whether the incoming call should be disconnected as if the user had manually 184 * rejected it. 185 */ getRejectCall()186 public boolean getRejectCall() { 187 return mShouldRejectCall; 188 } 189 190 /* 191 * @return Whether the ringtone should be silenced for the incoming call. 192 */ getSilenceCall()193 public boolean getSilenceCall() { 194 return mShouldSilenceCall; 195 } 196 197 /* 198 * @return Whether the incoming call should not be displayed in the call log. 199 */ getSkipCallLog()200 public boolean getSkipCallLog() { 201 return mShouldSkipCallLog; 202 } 203 204 /* 205 * @return Whether a missed call notification should not be shown for the incoming call. 206 */ getSkipNotification()207 public boolean getSkipNotification() { 208 return mShouldSkipNotification; 209 } 210 211 /** 212 * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow 213 * for further screening of the call. 214 * @hide 215 */ getShouldScreenCallViaAudioProcessing()216 public boolean getShouldScreenCallViaAudioProcessing() { 217 return mShouldScreenCallViaAudioProcessing; 218 } 219 220 public static class Builder { 221 private boolean mShouldDisallowCall; 222 private boolean mShouldRejectCall; 223 private boolean mShouldSilenceCall; 224 private boolean mShouldSkipCallLog; 225 private boolean mShouldSkipNotification; 226 private boolean mShouldScreenCallViaAudioProcessing; 227 228 /** 229 * Sets whether the incoming call should be blocked. 230 */ setDisallowCall(boolean shouldDisallowCall)231 public Builder setDisallowCall(boolean shouldDisallowCall) { 232 mShouldDisallowCall = shouldDisallowCall; 233 return this; 234 } 235 236 /** 237 * Sets whether the incoming call should be disconnected as if the user had manually 238 * rejected it. This property should only be set to true if the call is disallowed. 239 */ setRejectCall(boolean shouldRejectCall)240 public Builder setRejectCall(boolean shouldRejectCall) { 241 mShouldRejectCall = shouldRejectCall; 242 return this; 243 } 244 245 /** 246 * Sets whether ringing should be silenced for the incoming call. When set 247 * to {@code true}, the Telecom framework will not play a ringtone for the call. 248 * The call will, however, still be sent to the default dialer app if it is not blocked. 249 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is 250 * still surfaced to the user, but in a less intrusive manner. 251 * 252 * Setting this to true only makes sense when the call has not been disallowed 253 * using {@link #setDisallowCall(boolean)}. 254 */ setSilenceCall(boolean shouldSilenceCall)255 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) { 256 mShouldSilenceCall = shouldSilenceCall; 257 return this; 258 } 259 260 /** 261 * Sets whether the incoming call should not be displayed in the call log. This property 262 * should only be set to true if the call is disallowed. 263 * <p> 264 * Note: Calls will still be logged with type 265 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property 266 * is set. 267 */ setSkipCallLog(boolean shouldSkipCallLog)268 public Builder setSkipCallLog(boolean shouldSkipCallLog) { 269 mShouldSkipCallLog = shouldSkipCallLog; 270 return this; 271 } 272 273 /** 274 * Sets whether a missed call notification should not be shown for the incoming call. 275 * This property should only be set to true if the call is disallowed. 276 */ setSkipNotification(boolean shouldSkipNotification)277 public Builder setSkipNotification(boolean shouldSkipNotification) { 278 mShouldSkipNotification = shouldSkipNotification; 279 return this; 280 } 281 282 /** 283 * Sets whether to request background audio processing so that the in-call service can 284 * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be 285 * called with {@code false}, and all other parameters in this builder will be ignored. 286 * 287 * This request will only be honored if the {@link CallScreeningService} shares the same 288 * uid as the default dialer app. Otherwise, the call will go through as usual. 289 * 290 * @param shouldScreenCallViaAudioProcessing Whether to request further call screening. 291 * @hide 292 */ 293 @SystemApi 294 @TestApi 295 @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT) setShouldScreenCallViaAudioProcessing( boolean shouldScreenCallViaAudioProcessing)296 public @NonNull Builder setShouldScreenCallViaAudioProcessing( 297 boolean shouldScreenCallViaAudioProcessing) { 298 mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing; 299 return this; 300 } 301 build()302 public CallResponse build() { 303 return new CallResponse( 304 mShouldDisallowCall, 305 mShouldRejectCall, 306 mShouldSilenceCall, 307 mShouldSkipCallLog, 308 mShouldSkipNotification, 309 mShouldScreenCallViaAudioProcessing); 310 } 311 } 312 } 313 CallScreeningService()314 public CallScreeningService() { 315 } 316 317 @Override onBind(Intent intent)318 public IBinder onBind(Intent intent) { 319 Log.v(this, "onBind"); 320 return new CallScreeningBinder(); 321 } 322 323 @Override onUnbind(Intent intent)324 public boolean onUnbind(Intent intent) { 325 Log.v(this, "onUnbind"); 326 return false; 327 } 328 329 /** 330 * Called when a new incoming or outgoing call is added which is not in the user's contact list. 331 * <p> 332 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by 333 * calling 334 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}. 335 * Your app can tell if a call is an incoming call by checking to see if 336 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}. 337 * <p> 338 * Note: The {@link Call.Details} instance provided to a call screening service will only have 339 * the following properties set. The rest of the {@link Call.Details} properties will be set to 340 * their default value or {@code null}. 341 * <ul> 342 * <li>{@link Call.Details#getCallDirection()}</li> 343 * <li>{@link Call.Details#getConnectTimeMillis()}</li> 344 * <li>{@link Call.Details#getCreationTimeMillis()}</li> 345 * <li>{@link Call.Details#getHandle()}</li> 346 * <li>{@link Call.Details#getHandlePresentation()}</li> 347 * </ul> 348 * <p> 349 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme} 350 * is {@link PhoneAccount#SCHEME_TEL} are passed for call 351 * screening. Further, only calls which are not in the user's contacts are passed for 352 * screening. For outgoing calls, no post-dial digits are passed. 353 * 354 * @param callDetails Information about a new call, see {@link Call.Details}. 355 */ onScreenCall(@onNull Call.Details callDetails)356 public abstract void onScreenCall(@NonNull Call.Details callDetails); 357 358 /** 359 * Responds to the given incoming call, either allowing it, silencing it or disallowing it. 360 * <p> 361 * The {@link CallScreeningService} calls this method to inform the system whether the call 362 * should be silently blocked or not. In the event that it should not be blocked, it may 363 * also be requested to ring silently. 364 * <p> 365 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is 366 * {@link Call.Details#DIRECTION_INCOMING}. 367 * 368 * @param callDetails The call to allow. 369 * <p> 370 * Must be the same {@link Call.Details call} which was provided to the 371 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}. 372 * @param response The {@link CallScreeningService.CallResponse} which contains information 373 * about how to respond to a call. 374 */ respondToCall(@onNull Call.Details callDetails, @NonNull CallResponse response)375 public final void respondToCall(@NonNull Call.Details callDetails, 376 @NonNull CallResponse response) { 377 try { 378 if (response.getDisallowCall()) { 379 mCallScreeningAdapter.disallowCall( 380 callDetails.getTelecomCallId(), 381 response.getRejectCall(), 382 !response.getSkipCallLog(), 383 !response.getSkipNotification(), 384 new ComponentName(getPackageName(), getClass().getName())); 385 } else if (response.getSilenceCall()) { 386 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId()); 387 } else if (response.getShouldScreenCallViaAudioProcessing()) { 388 mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId()); 389 } else { 390 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); 391 } 392 } catch (RemoteException e) { 393 } 394 } 395 } 396