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.SdkConstant; 20 import android.app.Service; 21 import android.content.Intent; 22 import android.os.Handler; 23 import android.os.IBinder; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.os.RemoteException; 27 28 import com.android.internal.os.SomeArgs; 29 import com.android.internal.telecom.ICallScreeningService; 30 import com.android.internal.telecom.ICallScreeningAdapter; 31 32 /** 33 * This service can be implemented by the default dialer (see 34 * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before 35 * they are shown to a user. 36 * <p> 37 * Below is an example manifest registration for a {@code CallScreeningService}. 38 * <pre> 39 * {@code 40 * <service android:name="your.package.YourCallScreeningServiceImplementation" 41 * android:permission="android.permission.BIND_SCREENING_SERVICE"> 42 * <intent-filter> 43 * <action android:name="android.telecom.CallScreeningService"/> 44 * </intent-filter> 45 * </service> 46 * } 47 * </pre> 48 */ 49 public abstract class CallScreeningService extends Service { 50 /** 51 * The {@link Intent} that must be declared as handled by the service. 52 */ 53 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 54 public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService"; 55 56 private static final int MSG_SCREEN_CALL = 1; 57 58 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 59 @Override 60 public void handleMessage(Message msg) { 61 switch (msg.what) { 62 case MSG_SCREEN_CALL: 63 SomeArgs args = (SomeArgs) msg.obj; 64 try { 65 mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1; 66 onScreenCall( 67 Call.Details.createFromParcelableCall((ParcelableCall) args.arg2)); 68 } finally { 69 args.recycle(); 70 } 71 break; 72 } 73 } 74 }; 75 76 private final class CallScreeningBinder extends ICallScreeningService.Stub { 77 @Override screenCall(ICallScreeningAdapter adapter, ParcelableCall call)78 public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) { 79 Log.v(this, "screenCall"); 80 SomeArgs args = SomeArgs.obtain(); 81 args.arg1 = adapter; 82 args.arg2 = call; 83 mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget(); 84 } 85 } 86 87 private ICallScreeningAdapter mCallScreeningAdapter; 88 89 /* 90 * Information about how to respond to an incoming call. 91 */ 92 public static class CallResponse { 93 private final boolean mShouldDisallowCall; 94 private final boolean mShouldRejectCall; 95 private final boolean mShouldSkipCallLog; 96 private final boolean mShouldSkipNotification; 97 CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, boolean shouldSkipCallLog, boolean shouldSkipNotification)98 private CallResponse( 99 boolean shouldDisallowCall, 100 boolean shouldRejectCall, 101 boolean shouldSkipCallLog, 102 boolean shouldSkipNotification) { 103 if (!shouldDisallowCall 104 && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) { 105 throw new IllegalStateException("Invalid response state for allowed call."); 106 } 107 108 mShouldDisallowCall = shouldDisallowCall; 109 mShouldRejectCall = shouldRejectCall; 110 mShouldSkipCallLog = shouldSkipCallLog; 111 mShouldSkipNotification = shouldSkipNotification; 112 } 113 114 /* 115 * @return Whether the incoming call should be blocked. 116 */ getDisallowCall()117 public boolean getDisallowCall() { 118 return mShouldDisallowCall; 119 } 120 121 /* 122 * @return Whether the incoming call should be disconnected as if the user had manually 123 * rejected it. 124 */ getRejectCall()125 public boolean getRejectCall() { 126 return mShouldRejectCall; 127 } 128 129 /* 130 * @return Whether the incoming call should not be displayed in the call log. 131 */ getSkipCallLog()132 public boolean getSkipCallLog() { 133 return mShouldSkipCallLog; 134 } 135 136 /* 137 * @return Whether a missed call notification should not be shown for the incoming call. 138 */ getSkipNotification()139 public boolean getSkipNotification() { 140 return mShouldSkipNotification; 141 } 142 143 public static class Builder { 144 private boolean mShouldDisallowCall; 145 private boolean mShouldRejectCall; 146 private boolean mShouldSkipCallLog; 147 private boolean mShouldSkipNotification; 148 149 /* 150 * Sets whether the incoming call should be blocked. 151 */ setDisallowCall(boolean shouldDisallowCall)152 public Builder setDisallowCall(boolean shouldDisallowCall) { 153 mShouldDisallowCall = shouldDisallowCall; 154 return this; 155 } 156 157 /* 158 * Sets whether the incoming call should be disconnected as if the user had manually 159 * rejected it. This property should only be set to true if the call is disallowed. 160 */ setRejectCall(boolean shouldRejectCall)161 public Builder setRejectCall(boolean shouldRejectCall) { 162 mShouldRejectCall = shouldRejectCall; 163 return this; 164 } 165 166 /* 167 * Sets whether the incoming call should not be displayed in the call log. This property 168 * should only be set to true if the call is disallowed. 169 */ setSkipCallLog(boolean shouldSkipCallLog)170 public Builder setSkipCallLog(boolean shouldSkipCallLog) { 171 mShouldSkipCallLog = shouldSkipCallLog; 172 return this; 173 } 174 175 /* 176 * Sets whether a missed call notification should not be shown for the incoming call. 177 * This property should only be set to true if the call is disallowed. 178 */ setSkipNotification(boolean shouldSkipNotification)179 public Builder setSkipNotification(boolean shouldSkipNotification) { 180 mShouldSkipNotification = shouldSkipNotification; 181 return this; 182 } 183 build()184 public CallResponse build() { 185 return new CallResponse( 186 mShouldDisallowCall, 187 mShouldRejectCall, 188 mShouldSkipCallLog, 189 mShouldSkipNotification); 190 } 191 } 192 } 193 CallScreeningService()194 public CallScreeningService() { 195 } 196 197 @Override onBind(Intent intent)198 public IBinder onBind(Intent intent) { 199 Log.v(this, "onBind"); 200 return new CallScreeningBinder(); 201 } 202 203 @Override onUnbind(Intent intent)204 public boolean onUnbind(Intent intent) { 205 Log.v(this, "onUnbind"); 206 return false; 207 } 208 209 /** 210 * Called when a new incoming call is added. 211 * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)} 212 * should be called to allow or disallow the call. 213 * 214 * @param callDetails Information about a new incoming call, see {@link Call.Details}. 215 */ onScreenCall(Call.Details callDetails)216 public abstract void onScreenCall(Call.Details callDetails); 217 218 /** 219 * Responds to the given call, either allowing it or disallowing it. 220 * 221 * @param callDetails The call to allow. 222 * @param response The {@link CallScreeningService.CallResponse} which contains information 223 * about how to respond to a call. 224 */ respondToCall(Call.Details callDetails, CallResponse response)225 public final void respondToCall(Call.Details callDetails, CallResponse response) { 226 try { 227 if (response.getDisallowCall()) { 228 mCallScreeningAdapter.disallowCall( 229 callDetails.getTelecomCallId(), 230 response.getRejectCall(), 231 !response.getSkipCallLog(), 232 !response.getSkipNotification()); 233 } else { 234 mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); 235 } 236 } catch (RemoteException e) { 237 } 238 } 239 } 240