|  | /* | 
|  | * Copyright (C) 2016 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License | 
|  | */ | 
|  |  | 
|  | package android.telecom; | 
|  |  | 
|  | import android.annotation.SdkConstant; | 
|  | import android.app.Service; | 
|  | import android.content.Intent; | 
|  | import android.os.Handler; | 
|  | import android.os.IBinder; | 
|  | import android.os.Looper; | 
|  | import android.os.Message; | 
|  | import android.os.RemoteException; | 
|  |  | 
|  | import com.android.internal.os.SomeArgs; | 
|  | import com.android.internal.telecom.ICallScreeningService; | 
|  | import com.android.internal.telecom.ICallScreeningAdapter; | 
|  |  | 
|  | /** | 
|  | * This service can be implemented by the default dialer (see | 
|  | * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before | 
|  | * they are shown to a user. | 
|  | * <p> | 
|  | * Below is an example manifest registration for a {@code CallScreeningService}. | 
|  | * <pre> | 
|  | * {@code | 
|  | * <service android:name="your.package.YourCallScreeningServiceImplementation" | 
|  | *          android:permission="android.permission.BIND_SCREENING_SERVICE"> | 
|  | *      <intent-filter> | 
|  | *          <action android:name="android.telecom.CallScreeningService"/> | 
|  | *      </intent-filter> | 
|  | * </service> | 
|  | * } | 
|  | * </pre> | 
|  | */ | 
|  | public abstract class CallScreeningService extends Service { | 
|  | /** | 
|  | * The {@link Intent} that must be declared as handled by the service. | 
|  | */ | 
|  | @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) | 
|  | public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService"; | 
|  |  | 
|  | private static final int MSG_SCREEN_CALL = 1; | 
|  |  | 
|  | private final Handler mHandler = new Handler(Looper.getMainLooper()) { | 
|  | @Override | 
|  | public void handleMessage(Message msg) { | 
|  | switch (msg.what) { | 
|  | case MSG_SCREEN_CALL: | 
|  | SomeArgs args = (SomeArgs) msg.obj; | 
|  | try { | 
|  | mCallScreeningAdapter = (ICallScreeningAdapter) args.arg1; | 
|  | onScreenCall( | 
|  | Call.Details.createFromParcelableCall((ParcelableCall) args.arg2)); | 
|  | } finally { | 
|  | args.recycle(); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | private final class CallScreeningBinder extends ICallScreeningService.Stub { | 
|  | @Override | 
|  | public void screenCall(ICallScreeningAdapter adapter, ParcelableCall call) { | 
|  | Log.v(this, "screenCall"); | 
|  | SomeArgs args = SomeArgs.obtain(); | 
|  | args.arg1 = adapter; | 
|  | args.arg2 = call; | 
|  | mHandler.obtainMessage(MSG_SCREEN_CALL, args).sendToTarget(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private ICallScreeningAdapter mCallScreeningAdapter; | 
|  |  | 
|  | /* | 
|  | * Information about how to respond to an incoming call. | 
|  | */ | 
|  | public static class CallResponse { | 
|  | private final boolean mShouldDisallowCall; | 
|  | private final boolean mShouldRejectCall; | 
|  | private final boolean mShouldSkipCallLog; | 
|  | private final boolean mShouldSkipNotification; | 
|  |  | 
|  | private CallResponse( | 
|  | boolean shouldDisallowCall, | 
|  | boolean shouldRejectCall, | 
|  | boolean shouldSkipCallLog, | 
|  | boolean shouldSkipNotification) { | 
|  | if (!shouldDisallowCall | 
|  | && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) { | 
|  | throw new IllegalStateException("Invalid response state for allowed call."); | 
|  | } | 
|  |  | 
|  | mShouldDisallowCall = shouldDisallowCall; | 
|  | mShouldRejectCall = shouldRejectCall; | 
|  | mShouldSkipCallLog = shouldSkipCallLog; | 
|  | mShouldSkipNotification = shouldSkipNotification; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * @return Whether the incoming call should be blocked. | 
|  | */ | 
|  | public boolean getDisallowCall() { | 
|  | return mShouldDisallowCall; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * @return Whether the incoming call should be disconnected as if the user had manually | 
|  | * rejected it. | 
|  | */ | 
|  | public boolean getRejectCall() { | 
|  | return mShouldRejectCall; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * @return Whether the incoming call should not be displayed in the call log. | 
|  | */ | 
|  | public boolean getSkipCallLog() { | 
|  | return mShouldSkipCallLog; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * @return Whether a missed call notification should not be shown for the incoming call. | 
|  | */ | 
|  | public boolean getSkipNotification() { | 
|  | return mShouldSkipNotification; | 
|  | } | 
|  |  | 
|  | public static class Builder { | 
|  | private boolean mShouldDisallowCall; | 
|  | private boolean mShouldRejectCall; | 
|  | private boolean mShouldSkipCallLog; | 
|  | private boolean mShouldSkipNotification; | 
|  |  | 
|  | /* | 
|  | * Sets whether the incoming call should be blocked. | 
|  | */ | 
|  | public Builder setDisallowCall(boolean shouldDisallowCall) { | 
|  | mShouldDisallowCall = shouldDisallowCall; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets whether the incoming call should be disconnected as if the user had manually | 
|  | * rejected it. This property should only be set to true if the call is disallowed. | 
|  | */ | 
|  | public Builder setRejectCall(boolean shouldRejectCall) { | 
|  | mShouldRejectCall = shouldRejectCall; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets whether the incoming call should not be displayed in the call log. This property | 
|  | * should only be set to true if the call is disallowed. | 
|  | */ | 
|  | public Builder setSkipCallLog(boolean shouldSkipCallLog) { | 
|  | mShouldSkipCallLog = shouldSkipCallLog; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Sets whether a missed call notification should not be shown for the incoming call. | 
|  | * This property should only be set to true if the call is disallowed. | 
|  | */ | 
|  | public Builder setSkipNotification(boolean shouldSkipNotification) { | 
|  | mShouldSkipNotification = shouldSkipNotification; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public CallResponse build() { | 
|  | return new CallResponse( | 
|  | mShouldDisallowCall, | 
|  | mShouldRejectCall, | 
|  | mShouldSkipCallLog, | 
|  | mShouldSkipNotification); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public CallScreeningService() { | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public IBinder onBind(Intent intent) { | 
|  | Log.v(this, "onBind"); | 
|  | return new CallScreeningBinder(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean onUnbind(Intent intent) { | 
|  | Log.v(this, "onUnbind"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Called when a new incoming call is added. | 
|  | * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)} | 
|  | * should be called to allow or disallow the call. | 
|  | * | 
|  | * @param callDetails Information about a new incoming call, see {@link Call.Details}. | 
|  | */ | 
|  | public abstract void onScreenCall(Call.Details callDetails); | 
|  |  | 
|  | /** | 
|  | * Responds to the given call, either allowing it or disallowing it. | 
|  | * | 
|  | * @param callDetails The call to allow. | 
|  | * @param response The {@link CallScreeningService.CallResponse} which contains information | 
|  | * about how to respond to a call. | 
|  | */ | 
|  | public final void respondToCall(Call.Details callDetails, CallResponse response) { | 
|  | try { | 
|  | if (response.getDisallowCall()) { | 
|  | mCallScreeningAdapter.disallowCall( | 
|  | callDetails.getTelecomCallId(), | 
|  | response.getRejectCall(), | 
|  | !response.getSkipCallLog(), | 
|  | !response.getSkipNotification()); | 
|  | } else { | 
|  | mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); | 
|  | } | 
|  | } catch (RemoteException e) { | 
|  | } | 
|  | } | 
|  | } |