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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SdkConstant; 22 import android.app.Service; 23 import android.content.ComponentName; 24 import android.content.Intent; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.RemoteException; 31 32 import com.android.internal.os.SomeArgs; 33 import com.android.internal.telecom.ICallScreeningAdapter; 34 import com.android.internal.telecom.ICallScreeningService; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 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("android.app.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 onScreenCall( 112 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2)); 113 } finally { 114 args.recycle(); 115 } 116 break; 117 } 118 } 119 }; 120 121 private final class CallScreeningBinder extends ICallScreeningService.Stub { 122 @Override screenCall(ICallScreeningAdapter adapter, ParcelableCall call)123 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) { 124 Log.v(this, "screenCall"); 125 SomeArgs args = SomeArgs.obtain(); 126 args.arg1 = adapter; 127 args.arg2 = call; 128 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget(); 129 } 130 } 131 132 private ICallScreeningAdapter mCallScreeningAdapter; 133 134 /* 135 * Information about how to respond to an incoming call. 136 */ 137 public static class CallResponse { 138 private final boolean mShouldDisallowCall; 139 private final boolean mShouldRejectCall; 140 private final boolean mShouldSilenceCall; 141 private final boolean mShouldSkipCallLog; 142 private final boolean mShouldSkipNotification; 143 CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification)144 private CallResponse( 145 boolean shouldDisallowCall, 146 boolean shouldRejectCall, 147 boolean shouldSilenceCall, 148 boolean shouldSkipCallLog, 149 boolean shouldSkipNotification) { 150 if (!shouldDisallowCall 151 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) { 152 throw new IllegalStateException("Invalid response state for allowed call."); 153 } 154 155 mShouldDisallowCall = shouldDisallowCall; 156 mShouldRejectCall = shouldRejectCall; 157 mShouldSkipCallLog = shouldSkipCallLog; 158 mShouldSkipNotification = shouldSkipNotification; 159 mShouldSilenceCall = shouldSilenceCall; 160 } 161 162 /* 163 * @return Whether the incoming call should be blocked. 164 */ getDisallowCall()165 public boolean getDisallowCall() { 166 return mShouldDisallowCall; 167 } 168 169 /* 170 * @return Whether the incoming call should be disconnected as if the user had manually 171 * rejected it. 172 */ getRejectCall()173 public boolean getRejectCall() { 174 return mShouldRejectCall; 175 } 176 177 /* 178 * @return Whether the ringtone should be silenced for the incoming call. 179 */ getSilenceCall()180 public boolean getSilenceCall() { 181 return mShouldSilenceCall; 182 } 183 184 /* 185 * @return Whether the incoming call should not be displayed in the call log. 186 */ getSkipCallLog()187 public boolean getSkipCallLog() { 188 return mShouldSkipCallLog; 189 } 190 191 /* 192 * @return Whether a missed call notification should not be shown for the incoming call. 193 */ getSkipNotification()194 public boolean getSkipNotification() { 195 return mShouldSkipNotification; 196 } 197 198 public static class Builder { 199 private boolean mShouldDisallowCall; 200 private boolean mShouldRejectCall; 201 private boolean mShouldSilenceCall; 202 private boolean mShouldSkipCallLog; 203 private boolean mShouldSkipNotification; 204 205 /** 206 * Sets whether the incoming call should be blocked. 207 */ setDisallowCall(boolean shouldDisallowCall)208 public Builder setDisallowCall(boolean shouldDisallowCall) { 209 mShouldDisallowCall = shouldDisallowCall; 210 return this; 211 } 212 213 /** 214 * Sets whether the incoming call should be disconnected as if the user had manually 215 * rejected it. This property should only be set to true if the call is disallowed. 216 */ setRejectCall(boolean shouldRejectCall)217 public Builder setRejectCall(boolean shouldRejectCall) { 218 mShouldRejectCall = shouldRejectCall; 219 return this; 220 } 221 222 /** 223 * Sets whether ringing should be silenced for the incoming call. When set 224 * to {@code true}, the Telecom framework will not play a ringtone for the call. 225 * The call will, however, still be sent to the default dialer app if it is not blocked. 226 * A {@link CallScreeningService} can use this to ensure a potential nuisance call is 227 * still surfaced to the user, but in a less intrusive manner. 228 * 229 * Setting this to true only makes sense when the call has not been disallowed 230 * using {@link #setDisallowCall(boolean)}. 231 */ setSilenceCall(boolean shouldSilenceCall)232 public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) { 233 mShouldSilenceCall = shouldSilenceCall; 234 return this; 235 } 236 237 /** 238 * Sets whether the incoming call should not be displayed in the call log. This property 239 * should only be set to true if the call is disallowed. 240 * <p> 241 * Note: Calls will still be logged with type 242 * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property 243 * is set. 244 */ setSkipCallLog(boolean shouldSkipCallLog)245 public Builder setSkipCallLog(boolean shouldSkipCallLog) { 246 mShouldSkipCallLog = shouldSkipCallLog; 247 return this; 248 } 249 250 /** 251 * Sets whether a missed call notification should not be shown for the incoming call. 252 * This property should only be set to true if the call is disallowed. 253 */ setSkipNotification(boolean shouldSkipNotification)254 public Builder setSkipNotification(boolean shouldSkipNotification) { 255 mShouldSkipNotification = shouldSkipNotification; 256 return this; 257 } 258 build()259 public CallResponse build() { 260 return new CallResponse( 261 mShouldDisallowCall, 262 mShouldRejectCall, 263 mShouldSilenceCall, 264 mShouldSkipCallLog, 265 mShouldSkipNotification); 266 } 267 } 268 } 269 CallScreeningService()270 public CallScreeningService() { 271 } 272 273 @Override onBind(Intent intent)274 public IBinder onBind(Intent intent) { 275 Log.v(this, "onBind"); 276 return new CallScreeningBinder(); 277 } 278 279 @Override onUnbind(Intent intent)280 public boolean onUnbind(Intent intent) { 281 Log.v(this, "onUnbind"); 282 return false; 283 } 284 285 /** 286 * Called when a new incoming or outgoing call is added which is not in the user's contact list. 287 * <p> 288 * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by 289 * calling 290 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}. 291 * Your app can tell if a call is an incoming call by checking to see if 292 * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}. 293 * <p> 294 * Note: The {@link Call.Details} instance provided to a call screening service will only have 295 * the following properties set. The rest of the {@link Call.Details} properties will be set to 296 * their default value or {@code null}. 297 * <ul> 298 * <li>{@link Call.Details#getCallDirection()}</li> 299 * <li>{@link Call.Details#getConnectTimeMillis()}</li> 300 * <li>{@link Call.Details#getCreationTimeMillis()}</li> 301 * <li>{@link Call.Details#getHandle()}</li> 302 * <li>{@link Call.Details#getHandlePresentation()}</li> 303 * </ul> 304 * <p> 305 * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme} 306 * is {@link PhoneAccount#SCHEME_TEL} are passed for call 307 * screening. Further, only calls which are not in the user's contacts are passed for 308 * screening. For outgoing calls, no post-dial digits are passed. 309 * 310 * @param callDetails Information about a new call, see {@link Call.Details}. 311 */ onScreenCall(@onNull Call.Details callDetails)312 public abstract void onScreenCall(@NonNull Call.Details callDetails); 313 314 /** 315 * Responds to the given incoming call, either allowing it, silencing it or disallowing it. 316 * <p> 317 * The {@link CallScreeningService} calls this method to inform the system whether the call 318 * should be silently blocked or not. In the event that it should not be blocked, it may 319 * also be requested to ring silently. 320 * <p> 321 * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is 322 * {@link Call.Details#DIRECTION_INCOMING}. 323 * 324 * @param callDetails The call to allow. 325 * <p> 326 * Must be the same {@link Call.Details call} which was provided to the 327 * {@link CallScreeningService} via {@link #onScreenCall(Call.Details)}. 328 * @param response The {@link CallScreeningService.CallResponse} which contains information 329 * about how to respond to a call. 330 */ respondToCall(@onNull Call.Details callDetails, @NonNull CallResponse response)331 public final void respondToCall(@NonNull Call.Details callDetails, 332 @NonNull CallResponse response) { 333 try { 334 if (response.getDisallowCall()) { 335 mCallScreeningAdapter.disallowCall( 336 callDetails.getTelecomCallId(), 337 response.getRejectCall(), 338 !response.getSkipCallLog(), 339 !response.getSkipNotification(), 340 new ComponentName(getPackageName(), getClass().getName())); 341 } else if (response.getSilenceCall()) { 342 mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId()); 343 } else { 344 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); 345 } 346 } catch (RemoteException e) { 347 } 348 } 349 } 350