1 /* 2 * Copyright (C) 2018 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 com.android.server.telecom; 18 19 import android.Manifest; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.content.pm.ResolveInfo; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.os.UserHandle; 29 import android.telecom.CallScreeningService; 30 import android.telecom.Log; 31 import android.telecom.Logging.Session; 32 import android.text.TextUtils; 33 34 import com.android.internal.telecom.ICallScreeningAdapter; 35 import com.android.internal.telecom.ICallScreeningService; 36 37 import java.util.List; 38 import java.util.concurrent.CompletableFuture; 39 40 /** 41 * Helper class for performing operations with {@link CallScreeningService}s. 42 */ 43 public class CallScreeningServiceHelper { 44 private static final String TAG = CallScreeningServiceHelper.class.getSimpleName(); 45 46 /** 47 * Implementation of {@link CallScreeningService} adapter AIDL; provides a means for responses 48 * from the call screening service to be handled. 49 */ 50 private class CallScreeningAdapter extends ICallScreeningAdapter.Stub { 51 private ServiceConnection mServiceConnection; 52 CallScreeningAdapter(ServiceConnection connection)53 public CallScreeningAdapter(ServiceConnection connection) { 54 mServiceConnection = connection; 55 } 56 57 @Override onScreeningResponse(String callId, ComponentName componentName, CallScreeningService.ParcelableCallResponse callResponse)58 public void onScreeningResponse(String callId, ComponentName componentName, 59 CallScreeningService.ParcelableCallResponse callResponse) { 60 unbindCallScreeningService(); 61 } 62 unbindCallScreeningService()63 private void unbindCallScreeningService() { 64 mContext.unbindService(mServiceConnection); 65 } 66 } 67 68 private final ParcelableCallUtils.Converter mParcelableCallUtilsConverter; 69 private final TelecomSystem.SyncRoot mTelecomLock; 70 private final Call mCall; 71 private final UserHandle mUserHandle; 72 private final Context mContext; 73 private final AppLabelProxy mAppLabelProxy; 74 private final Session mLoggingSession; 75 private CompletableFuture mFuture; 76 private String mPackageName; 77 CallScreeningServiceHelper(Context context, TelecomSystem.SyncRoot telecomLock, String packageName, ParcelableCallUtils.Converter converter, UserHandle userHandle, Call call, AppLabelProxy appLabelProxy)78 public CallScreeningServiceHelper(Context context, TelecomSystem.SyncRoot telecomLock, 79 String packageName, ParcelableCallUtils.Converter converter, 80 UserHandle userHandle, Call call, AppLabelProxy appLabelProxy) { 81 mContext = context; 82 mTelecomLock = telecomLock; 83 mParcelableCallUtilsConverter = converter; 84 mCall = call; 85 mUserHandle = userHandle; 86 mPackageName = packageName; 87 mAppLabelProxy = appLabelProxy; 88 mLoggingSession = Log.createSubsession(); 89 } 90 91 /** 92 * Builds a {@link CompletableFuture} which performs a bind to a {@link CallScreeningService} 93 * @return 94 */ process()95 public CompletableFuture process() { 96 Log.d(this, "process"); 97 return bindAndGetCallIdentification(); 98 } 99 bindAndGetCallIdentification()100 public CompletableFuture bindAndGetCallIdentification() { 101 Log.d(this, "bindAndGetCallIdentification"); 102 if (mPackageName == null) { 103 return CompletableFuture.completedFuture(null); 104 } 105 106 mFuture = new CompletableFuture(); 107 108 ServiceConnection serviceConnection = new ServiceConnection() { 109 @Override 110 public void onServiceConnected(ComponentName name, IBinder service) { 111 ICallScreeningService screeningService = 112 ICallScreeningService.Stub.asInterface(service); 113 Log.continueSession(mLoggingSession, "CSSH.oSC"); 114 try { 115 try { 116 // Note: for outgoing calls, never include the restricted extras. 117 screeningService.screenCall(new CallScreeningAdapter(this), 118 mParcelableCallUtilsConverter.toParcelableCallForScreening(mCall, 119 false /* areRestrictedExtrasIncluded */)); 120 } catch (RemoteException e) { 121 Log.w(CallScreeningServiceHelper.this, 122 "Cancelling call id due to remote exception"); 123 mFuture.complete(null); 124 } 125 } finally { 126 Log.endSession(); 127 } 128 } 129 130 @Override 131 public void onServiceDisconnected(ComponentName name) { 132 // No locking needed -- CompletableFuture only lets one thread call complete. 133 Log.continueSession(mLoggingSession, "CSSH.oSD"); 134 try { 135 if (!mFuture.isDone()) { 136 Log.w(CallScreeningServiceHelper.this, 137 "Cancelling outgoing call screen due to service disconnect."); 138 } 139 mFuture.complete(null); 140 } finally { 141 Log.endSession(); 142 } 143 } 144 }; 145 146 if (!bindCallScreeningService(mContext, mUserHandle, mPackageName, serviceConnection)) { 147 Log.i(this, "bindAndGetCallIdentification - bind failed"); 148 Log.addEvent(mCall, LogUtils.Events.BIND_SCREENING, mPackageName); 149 mFuture.complete(null); 150 } 151 152 // Set up a timeout so that we're not waiting forever for the caller ID information. 153 Handler handler = new Handler(); 154 handler.postDelayed(() -> { 155 // No locking needed -- CompletableFuture only lets one thread call complete. 156 Log.continueSession(mLoggingSession, "CSSH.timeout"); 157 try { 158 if (!mFuture.isDone()) { 159 Log.w(TAG, "Cancelling call id process due to timeout"); 160 } 161 mFuture.complete(null); 162 } finally { 163 Log.endSession(); 164 } 165 }, 166 Timeouts.getCallScreeningTimeoutMillis(mContext.getContentResolver())); 167 return mFuture; 168 } 169 170 /** 171 * Binds to a {@link CallScreeningService}. 172 * @param context The current context. 173 * @param userHandle User to bind as. 174 * @param packageName Package name of the {@link CallScreeningService}. 175 * @param serviceConnection The {@link ServiceConnection} to be notified of binding. 176 * @return {@code true} if binding succeeds, {@code false} otherwise. 177 */ bindCallScreeningService(Context context, UserHandle userHandle, String packageName, ServiceConnection serviceConnection)178 public static boolean bindCallScreeningService(Context context, UserHandle userHandle, 179 String packageName, ServiceConnection serviceConnection) { 180 if (TextUtils.isEmpty(packageName)) { 181 Log.i(TAG, "PackageName is empty. Not performing call screening."); 182 return false; 183 } 184 185 Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE) 186 .setPackage(packageName); 187 List<ResolveInfo> entries = context.getPackageManager().queryIntentServicesAsUser( 188 intent, 0, userHandle.getIdentifier()); 189 if (entries.isEmpty()) { 190 Log.i(TAG, packageName + " has no call screening service defined."); 191 return false; 192 } 193 194 ResolveInfo entry = entries.get(0); 195 if (entry.serviceInfo == null) { 196 Log.w(TAG, packageName + " call screening service has invalid service info"); 197 return false; 198 } 199 200 if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals( 201 Manifest.permission.BIND_SCREENING_SERVICE)) { 202 Log.w(TAG, "CallScreeningService must require BIND_SCREENING_SERVICE permission: " + 203 entry.serviceInfo.packageName); 204 return false; 205 } 206 207 ComponentName componentName = 208 new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name); 209 intent.setComponent(componentName); 210 if (context.bindServiceAsUser( 211 intent, 212 serviceConnection, 213 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 214 UserHandle.CURRENT)) { 215 Log.d(TAG, "bindService, found service, waiting for it to connect"); 216 return true; 217 } 218 219 return false; 220 } 221 } 222