• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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