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