1 /*
2  * Copyright (C) 2014 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 static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
20 import static android.os.Process.myUid;
21 
22 import android.Manifest;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.AppOpsManager;
26 import android.app.KeyguardManager;
27 import android.app.Notification;
28 import android.app.NotificationManager;
29 import android.content.AttributionSource;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.PermissionChecker;
36 import android.content.ServiceConnection;
37 import android.content.pm.PackageManager;
38 import android.content.pm.ResolveInfo;
39 import android.content.pm.ServiceInfo;
40 import android.hardware.SensorPrivacyManager;
41 import android.os.Binder;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.IBinder;
45 import android.os.Looper;
46 import android.os.PackageTagsList;
47 import android.os.RemoteException;
48 import android.os.Trace;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.telecom.CallAudioState;
52 import android.telecom.CallEndpoint;
53 import android.telecom.ConnectionService;
54 import android.telecom.InCallService;
55 import android.telecom.Log;
56 import android.telecom.Logging.Runnable;
57 import android.telecom.ParcelableCall;
58 import android.telecom.TelecomManager;
59 import android.text.TextUtils;
60 import android.util.ArrayMap;
61 import android.util.ArraySet;
62 import android.util.Pair;
63 
64 import com.android.internal.annotations.VisibleForTesting;
65 // TODO: Needed for move to system service: import com.android.internal.R;
66 import com.android.internal.telecom.IInCallService;
67 import com.android.internal.util.ArrayUtils;
68 import com.android.internal.util.IndentingPrintWriter;
69 import com.android.server.telecom.SystemStateHelper.SystemStateListener;
70 import com.android.server.telecom.flags.FeatureFlags;
71 import com.android.server.telecom.ui.NotificationChannelManager;
72 
73 import java.util.ArrayList;
74 import java.util.Arrays;
75 import java.util.Collection;
76 import java.util.HashMap;
77 import java.util.LinkedList;
78 import java.util.List;
79 import java.util.Map;
80 import java.util.Objects;
81 import java.util.Set;
82 import java.util.UUID;
83 import java.util.concurrent.CompletableFuture;
84 import java.util.concurrent.TimeUnit;
85 import java.util.stream.Collectors;
86 import java.util.stream.Stream;
87 
88 /**
89  * Binds to {@link IInCallService} and provides the service to {@link CallsManager} through which it
90  * can send updates to the in-call app. This class is created and owned by CallsManager and retains
91  * a binding to the {@link IInCallService} (implemented by the in-call app).
92  */
93 public class InCallController extends CallsManagerListenerBase implements
94         AppOpsManager.OnOpActiveChangedListener {
95     public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName();
96     public static final int IN_CALL_SERVICE_NOTIFICATION_ID = 3;
97     private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
98 
99     /**
100      * Anomaly Report UUIDs and corresponding error descriptions specific to InCallController.
101      */
102     public static final UUID SET_IN_CALL_ADAPTER_ERROR_UUID =
103             UUID.fromString("0c2adf96-353a-433c-afe9-1e5564f304f9");
104     public static final String SET_IN_CALL_ADAPTER_ERROR_MSG =
105             "Exception thrown while setting the in-call adapter.";
106 
107     @VisibleForTesting
setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter)108     public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
109         mAnomalyReporter = mAnomalyReporterAdapter;
110     }
111 
112     public class InCallServiceConnection {
113         /**
114          * Indicates that a call to {@link #connect(Call)} has succeeded and resulted in a
115          * connection to an InCallService.
116          */
117         public static final int CONNECTION_SUCCEEDED = 1;
118         /**
119          * Indicates that a call to {@link #connect(Call)} has failed because of a binding issue.
120          */
121         public static final int CONNECTION_FAILED = 2;
122         /**
123          * Indicates that a call to {@link #connect(Call)} has been skipped because the
124          * IncallService does not support the type of call..
125          */
126         public static final int CONNECTION_NOT_SUPPORTED = 3;
127 
128         public class Listener {
onDisconnect(InCallServiceConnection conn, Call call)129             public void onDisconnect(InCallServiceConnection conn, Call call) {}
130         }
131 
132         protected Listener mListener;
133 
connect(Call call)134         public int connect(Call call) { return CONNECTION_FAILED; }
disconnect()135         public void disconnect() {}
isConnected()136         public boolean isConnected() { return false; }
setHasEmergency(boolean hasEmergency)137         public void setHasEmergency(boolean hasEmergency) {}
setListener(Listener l)138         public void setListener(Listener l) {
139             mListener = l;
140         }
getInfo()141         public InCallServiceInfo getInfo() { return null; }
dump(IndentingPrintWriter pw)142         public void dump(IndentingPrintWriter pw) {}
143         public Call mCall;
144     }
145 
146     public static class InCallServiceInfo {
147         private final ComponentName mComponentName;
148         private boolean mIsExternalCallsSupported;
149         private boolean mIsSelfManagedCallsSupported;
150         private final int mType;
151         private long mBindingStartTime;
152         private long mDisconnectTime;
153 
154         private boolean mHasCrossUserOrProfilePerm;
155 
InCallServiceInfo(ComponentName componentName, boolean isExternalCallsSupported, boolean isSelfManageCallsSupported, int type, boolean hasCrossUserOrProfilePerm)156         public InCallServiceInfo(ComponentName componentName,
157                 boolean isExternalCallsSupported,
158                 boolean isSelfManageCallsSupported,
159                 int type, boolean hasCrossUserOrProfilePerm) {
160             mComponentName = componentName;
161             mIsExternalCallsSupported = isExternalCallsSupported;
162             mIsSelfManagedCallsSupported = isSelfManageCallsSupported;
163             mType = type;
164             mHasCrossUserOrProfilePerm = hasCrossUserOrProfilePerm;
165         }
166 
hasCrossUserOrProfilePermission()167         public boolean hasCrossUserOrProfilePermission() { return mHasCrossUserOrProfilePerm; }
getComponentName()168         public ComponentName getComponentName() {
169             return mComponentName;
170         }
171 
isExternalCallsSupported()172         public boolean isExternalCallsSupported() {
173             return mIsExternalCallsSupported;
174         }
175 
isSelfManagedCallsSupported()176         public boolean isSelfManagedCallsSupported() {
177             return mIsSelfManagedCallsSupported;
178         }
179 
getType()180         public int getType() {
181             return mType;
182         }
183 
getBindingStartTime()184         public long getBindingStartTime() {
185             return mBindingStartTime;
186         }
187 
getDisconnectTime()188         public long getDisconnectTime() {
189             return mDisconnectTime;
190         }
191 
setBindingStartTime(long bindingStartTime)192         public void setBindingStartTime(long bindingStartTime) {
193             mBindingStartTime = bindingStartTime;
194         }
195 
setDisconnectTime(long disconnectTime)196         public void setDisconnectTime(long disconnectTime) {
197             mDisconnectTime = disconnectTime;
198         }
199 
200         @Override
equals(Object o)201         public boolean equals(Object o) {
202             if (this == o) {
203                 return true;
204             }
205             if (o == null || getClass() != o.getClass()) {
206                 return false;
207             }
208 
209             InCallServiceInfo that = (InCallServiceInfo) o;
210 
211             if (mIsExternalCallsSupported != that.mIsExternalCallsSupported) {
212                 return false;
213             }
214             if (mIsSelfManagedCallsSupported != that.mIsSelfManagedCallsSupported) {
215                 return false;
216             }
217             return mComponentName.equals(that.mComponentName);
218 
219         }
220 
221         @Override
hashCode()222         public int hashCode() {
223             return Objects.hash(mComponentName, mIsExternalCallsSupported,
224                     mIsSelfManagedCallsSupported);
225         }
226 
227         @Override
toString()228         public String toString() {
229             return "[" + mComponentName + " supportsExternal? " + mIsExternalCallsSupported +
230                     " supportsSelfMg?" + mIsSelfManagedCallsSupported + "]";
231         }
232     }
233 
234     private class InCallServiceBindingConnection extends InCallServiceConnection {
235 
236         private final ServiceConnection mServiceConnection = new ServiceConnection() {
237             @Override
238             public void onServiceConnected(ComponentName name, IBinder service) {
239                 Log.startSession("ICSBC.oSC", Log.getPackageAbbreviation(name));
240                 synchronized (mLock) {
241                     try {
242                         Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
243                         mIsBound = true;
244                         if (mIsConnected) {
245                             // Only proceed if we are supposed to be connected.
246                             onConnected(service);
247                         }
248                     } finally {
249                         Log.endSession();
250                     }
251                 }
252             }
253 
254             @Override
255             public void onServiceDisconnected(ComponentName name) {
256                 Log.startSession("ICSBC.oSD", Log.getPackageAbbreviation(name));
257                 synchronized (mLock) {
258                     try {
259                         Log.d(this, "onServiceDisconnected: %s", name);
260                         mIsBound = false;
261                         onDisconnected();
262                     } finally {
263                         Log.endSession();
264                     }
265                 }
266             }
267 
268             @Override
269             public void onNullBinding(ComponentName name) {
270                 Log.startSession("ICSBC.oNB", Log.getPackageAbbreviation(name));
271                 synchronized (mLock) {
272                     try {
273                         Log.d(this, "onNullBinding: %s", name);
274                         mIsNullBinding = true;
275                         mIsBound = false;
276                         onDisconnected();
277                     } finally {
278                         Log.endSession();
279                     }
280                 }
281             }
282 
283             @Override
284             public void onBindingDied(ComponentName name) {
285                 Log.startSession("ICSBC.oBD", Log.getPackageAbbreviation(name));
286                 synchronized (mLock) {
287                     try {
288                         Log.d(this, "onBindingDied: %s", name);
289                         mIsBound = false;
290                         onDisconnected();
291                     } finally {
292                         Log.endSession();
293                     }
294                 }
295             }
296         };
297 
298         private final InCallServiceInfo mInCallServiceInfo;
299         private boolean mIsConnected = false;
300         private boolean mIsBound = false;
301         private boolean mIsNullBinding = false;
302         private NotificationManager mNotificationManager;
303 
304         //this is really used for cases where the userhandle for a call
305         //does not match what we want to use for bindAsUser
306         private final UserHandle mUserHandleToUseForBinding;
307 
InCallServiceBindingConnection(InCallServiceInfo info)308         public InCallServiceBindingConnection(InCallServiceInfo info) {
309             mInCallServiceInfo = info;
310             mUserHandleToUseForBinding = null;
311         }
312 
InCallServiceBindingConnection(InCallServiceInfo info, UserHandle userHandleToUseForBinding)313         public InCallServiceBindingConnection(InCallServiceInfo info,
314                 UserHandle userHandleToUseForBinding) {
315             mInCallServiceInfo = info;
316             mUserHandleToUseForBinding = userHandleToUseForBinding;
317         }
318 
319         @Override
connect(Call call)320         public int connect(Call call) {
321             UserHandle userFromCall = getUserFromCall(call);
322 
323             if (mIsConnected) {
324                 Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request: "
325                         + mInCallServiceInfo);
326                 if (call != null) {
327                     // Track the call if we don't already know about it.
328                     addCall(call);
329 
330                     // Notify this new added call
331                     if (mFeatureFlags.separatelyBindToBtIncallService()
332                             && mInCallServiceInfo.getType() == IN_CALL_SERVICE_TYPE_BLUETOOTH) {
333                         sendCallToService(call, mInCallServiceInfo, mBTInCallServices
334                                 .get(userFromCall).second);
335                     } else {
336                         sendCallToService(call, mInCallServiceInfo,
337                                 mInCallServices.get(userFromCall).get(mInCallServiceInfo));
338                     }
339                 }
340                 return CONNECTION_SUCCEEDED;
341             }
342 
343             if (call != null && call.isSelfManaged() &&
344                     (!mInCallServiceInfo.isSelfManagedCallsSupported()
345                             || !call.visibleToInCallService())) {
346                 Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
347                         mInCallServiceInfo);
348                 mIsConnected = false;
349                 return CONNECTION_NOT_SUPPORTED;
350             }
351 
352             Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
353             intent.setComponent(mInCallServiceInfo.getComponentName());
354             if (call != null && !call.isIncoming() && !call.isExternalCall()) {
355                 intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
356                         call.getIntentExtras());
357                 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
358                         call.getTargetPhoneAccount());
359             }
360 
361             Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
362             mIsConnected = true;
363             mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
364             boolean isManagedProfile = UserUtil.isManagedProfile(mContext,
365                     userFromCall, mFeatureFlags);
366             // Note that UserHandle.CURRENT fails to capture the work profile, so we need to handle
367             // it separately to ensure that the ICS is bound to the appropriate user. If ECBM is
368             // active, we know that a work sim was previously used to place a MO emergency call. We
369             // need to ensure that we bind to the CURRENT_USER in this case, as the work user would
370             // not be running (handled in getUserFromCall).
371             UserHandle userToBind = isManagedProfile ? userFromCall : UserHandle.CURRENT;
372             if ((mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_NON_UI
373                     || mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
374                     || mInCallServiceInfo.mType == IN_CALL_SERVICE_TYPE_BLUETOOTH) && (
375                     mUserHandleToUseForBinding != null)) {
376                 //guarding change for non-UI/carmode-UI services which may not be present for
377                 // work profile.
378                 //In this case, we use the parent user handle. (This also seems to be more
379                 // accurate that USER_CURRENT since we queried/discovered the packages using the
380                 // parent handle)
381                 if (mInCallServiceInfo.hasCrossUserOrProfilePermission()) {
382                     userToBind = mUserHandleToUseForBinding;
383                 } else {
384                     Log.i(this,
385                             "service does not have INTERACT_ACROSS_PROFILES or "
386                                     + "INTERACT_ACROSS_USERS permission");
387                 }
388             }
389             Log.i(this, "using user id: %s for binding. User from Call is: %s", userToBind,
390                     userFromCall);
391             if (!mContext.bindServiceAsUser(intent, mServiceConnection,
392                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
393                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
394                         | Context.BIND_SCHEDULE_LIKE_TOP_APP, userToBind)) {
395                 Log.w(this, "Failed to connect.");
396                 mIsConnected = false;
397             }
398 
399             if (mIsConnected && call != null) {
400                 mCall = call;
401             }
402             Log.i(this, "mCall: %s, mIsConnected: %s", mCall, mIsConnected);
403 
404             return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
405         }
406 
407         @Override
getInfo()408         public InCallServiceInfo getInfo() {
409             return mInCallServiceInfo;
410         }
411 
412         @Override
disconnect()413         public void disconnect() {
414             if (mIsConnected) {
415                 UserHandle userFromCall = getUserFromCall(mCall);
416                 mInCallServiceInfo.setDisconnectTime(mClockProxy.elapsedRealtime());
417                 Log.i(InCallController.this, "ICSBC#disconnect: unbinding after %s ms;"
418                                 + "%s. isCrashed: %s", mInCallServiceInfo.mDisconnectTime
419                                 - mInCallServiceInfo.mBindingStartTime,
420                         mInCallServiceInfo, mIsNullBinding);
421                 String packageName = mInCallServiceInfo.getComponentName().getPackageName();
422                 mContext.unbindService(mServiceConnection);
423                 mIsConnected = false;
424                 if (mIsNullBinding && mInCallServiceInfo.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
425                     // Non-UI InCallServices are allowed to return null from onBind if they don't
426                     // want to handle calls at the moment, so don't report them to the user as
427                     // crashed.
428                     sendCrashedInCallServiceNotification(packageName, userFromCall);
429                 }
430                 if (mCall != null) {
431                     mCall.getAnalytics().addInCallService(
432                             mInCallServiceInfo.getComponentName().flattenToShortString(),
433                             mInCallServiceInfo.getType(),
434                             mInCallServiceInfo.getDisconnectTime()
435                                     - mInCallServiceInfo.getBindingStartTime(), mIsNullBinding);
436                     updateCallTracking(mCall, mInCallServiceInfo, false /* isAdd */);
437                 }
438 
439                 InCallController.this.onDisconnected(mInCallServiceInfo, userFromCall);
440             } else {
441                 Log.i(InCallController.this, "ICSBC#disconnect: already disconnected; %s",
442                         mInCallServiceInfo);
443                 Log.addEvent(null, LogUtils.Events.INFO, "Already disconnected, ignoring request.");
444             }
445         }
446 
447         @Override
isConnected()448         public boolean isConnected() {
449             return mIsConnected;
450         }
451 
452         @Override
dump(IndentingPrintWriter pw)453         public void dump(IndentingPrintWriter pw) {
454             pw.print("BindingConnection [");
455             pw.print(mIsConnected ? "" : "not ");
456             pw.print("connected, ");
457             pw.print(mIsBound ? "" : "not ");
458             pw.print("bound, ");
459             pw.print(mInCallServiceInfo);
460             pw.println("\n");
461         }
462 
onConnected(IBinder service)463         protected void onConnected(IBinder service) {
464             boolean shouldRemainConnected =
465                     InCallController.this.onConnected(mInCallServiceInfo, service,
466                             getUserFromCall(mCall));
467             if (!shouldRemainConnected) {
468                 // Sometimes we can opt to disconnect for certain reasons, like if the
469                 // InCallService rejected our initialization step, or the calls went away
470                 // in the time it took us to bind to the InCallService. In such cases, we go
471                 // ahead and disconnect ourselves.
472                 disconnect();
473             }
474         }
475 
onDisconnected()476         protected void onDisconnected() {
477             boolean shouldReconnect = mIsConnected;
478             InCallController.this.onDisconnected(mInCallServiceInfo, getUserFromCall(mCall));
479             disconnect();  // Unbind explicitly if we get disconnected.
480             if (mListener != null) {
481                 mListener.onDisconnect(InCallServiceBindingConnection.this, mCall);
482             }
483             // Check if we are expected to reconnect
484             if (shouldReconnect && shouldHandleReconnect()) {
485                 connect(mCall);  // reconnect
486             }
487         }
488 
shouldHandleReconnect()489         private boolean shouldHandleReconnect() {
490             int serviceType = mInCallServiceInfo.getType();
491             boolean nonUI = (serviceType == IN_CALL_SERVICE_TYPE_NON_UI)
492                     || (serviceType == IN_CALL_SERVICE_TYPE_COMPANION);
493             boolean carModeUI = (serviceType == IN_CALL_SERVICE_TYPE_CAR_MODE_UI);
494 
495             return carModeUI || (nonUI && !mIsNullBinding);
496         }
497     }
498 
499     /**
500      * A version of the InCallServiceBindingConnection that proxies all calls to a secondary
501      * connection until it finds an emergency call, or the other connection dies. When one of those
502      * two things happen, this class instance will take over the connection.
503      */
504     private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
505         private boolean mIsProxying = true;
506         private boolean mIsConnected = false;
507         private final InCallServiceConnection mSubConnection;
508 
509         private Listener mSubListener = new Listener() {
510             @Override
511             public void onDisconnect(InCallServiceConnection subConnection, Call call) {
512                 if (subConnection == mSubConnection) {
513                     if (mIsConnected && mIsProxying) {
514                         // At this point we know that we need to be connected to the InCallService
515                         // and we are proxying to the sub connection.  However, the sub-connection
516                         // just died so we need to stop proxying and connect to the system in-call
517                         // service instead.
518                         mIsProxying = false;
519                         connect(call);
520                     }
521                 }
522             }
523         };
524 
EmergencyInCallServiceConnection( InCallServiceInfo info, InCallServiceConnection subConnection)525         public EmergencyInCallServiceConnection(
526                 InCallServiceInfo info, InCallServiceConnection subConnection) {
527 
528             super(info);
529             mSubConnection = subConnection;
530             if (mSubConnection != null) {
531                 mSubConnection.setListener(mSubListener);
532             }
533             mIsProxying = (mSubConnection != null);
534         }
535 
536         @Override
connect(Call call)537         public int connect(Call call) {
538             mIsConnected = true;
539             if (mIsProxying) {
540                 int result = mSubConnection.connect(call);
541                 mIsConnected = result == CONNECTION_SUCCEEDED;
542                 if (result != CONNECTION_FAILED) {
543                     return result;
544                 }
545                 // Could not connect to child, stop proxying.
546                 mIsProxying = false;
547             }
548             UserHandle userFromCall = getUserFromCall(call);
549             mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
550                     userFromCall);
551 
552             if (call != null && call.isIncoming()
553                     && mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
554                 // Add the last emergency call time to the call
555                 Bundle extras = new Bundle();
556                 extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
557                         mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
558                 call.putConnectionServiceExtras(extras);
559             }
560 
561             // If we are here, we didn't or could not connect to child. So lets connect ourselves.
562             return super.connect(call);
563         }
564 
565         @Override
disconnect()566         public void disconnect() {
567             Log.i(this, "Disconnecting from InCallService");
568             if (mIsProxying) {
569                 mSubConnection.disconnect();
570             } else {
571                 super.disconnect();
572                 mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
573             }
574             mIsConnected = false;
575         }
576 
577         @Override
setHasEmergency(boolean hasEmergency)578         public void setHasEmergency(boolean hasEmergency) {
579             if (hasEmergency) {
580                 takeControl();
581             }
582         }
583 
584         @Override
getInfo()585         public InCallServiceInfo getInfo() {
586             if (mIsProxying) {
587                 return mSubConnection.getInfo();
588             } else {
589                 return super.getInfo();
590             }
591         }
592 
593         @Override
onDisconnected()594         protected void onDisconnected() {
595             // Save this here because super.onDisconnected() could force us to explicitly
596             // disconnect() as a cleanup step and that sets mIsConnected to false.
597             boolean shouldReconnect = mIsConnected;
598             super.onDisconnected();
599             // We just disconnected.  Check if we are expected to be connected, and reconnect.
600             if (shouldReconnect && !mIsProxying) {
601                 connect(mCall);  // reconnect
602             }
603         }
604 
605         @Override
dump(IndentingPrintWriter pw)606         public void dump(IndentingPrintWriter pw) {
607             pw.print("Emergency ICS Connection [");
608             pw.append(mIsProxying ? "" : "not ").append("proxying, ");
609             pw.append(mIsConnected ? "" : "not ").append("connected]\n");
610             pw.increaseIndent();
611             pw.print("Emergency: ");
612             super.dump(pw);
613             if (mSubConnection != null) {
614                 pw.print("Default-Dialer: ");
615                 mSubConnection.dump(pw);
616             }
617             pw.decreaseIndent();
618         }
619 
620         /**
621          * Forces the connection to take control from it's subConnection.
622          */
takeControl()623         private void takeControl() {
624             if (mIsProxying) {
625                 mIsProxying = false;
626                 if (mIsConnected) {
627                     mSubConnection.disconnect();
628                     super.connect(null);
629                 }
630             }
631         }
632     }
633 
634     /**
635      * A version of InCallServiceConnection which switches UI between two separate sub-instances of
636      * InCallServicesConnections.
637      */
638     private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
639         private final InCallServiceConnection mDialerConnection;
640         private InCallServiceConnection mCarModeConnection;
641         private InCallServiceConnection mCurrentConnection;
642         private boolean mIsCarMode = false;
643         private boolean mIsConnected = false;
644 
CarSwappingInCallServiceConnection( InCallServiceConnection dialerConnection, InCallServiceConnection carModeConnection)645         public CarSwappingInCallServiceConnection(
646                 InCallServiceConnection dialerConnection,
647                 InCallServiceConnection carModeConnection) {
648             mDialerConnection = dialerConnection;
649             mCarModeConnection = carModeConnection;
650             mCurrentConnection = getCurrentConnection();
651         }
652 
653         /**
654          * Called when we move to a state where calls are present on the device.  Chooses the
655          * {@link InCallService} to which we should connect.
656          *
657          * @param isCarMode {@code true} if device is in car mode, {@code false} otherwise.
658          */
chooseInitialInCallService(boolean isCarMode)659         public synchronized void chooseInitialInCallService(boolean isCarMode) {
660             Log.i(this, "chooseInitialInCallService: " + mIsCarMode + " => " + isCarMode);
661             if (isCarMode != mIsCarMode) {
662                 mIsCarMode = isCarMode;
663                 InCallServiceConnection newConnection = getCurrentConnection();
664                 if (newConnection != mCurrentConnection) {
665                     if (mIsConnected) {
666                         mCurrentConnection.disconnect();
667                     }
668                     int result = newConnection.connect(null);
669                     mIsConnected = result == CONNECTION_SUCCEEDED;
670                     mCurrentConnection = newConnection;
671                 }
672             }
673         }
674 
675         /**
676          * Invoked when {@link CarModeTracker} has determined that the device is no longer in car
677          * mode (i.e. has no car mode {@link InCallService}).
678          *
679          * Switches back to the default dialer app.
680          */
disableCarMode()681         public synchronized void disableCarMode() {
682             mIsCarMode = false;
683             if (mIsConnected) {
684                 mCurrentConnection.disconnect();
685             }
686 
687             mCurrentConnection = mDialerConnection;
688             int result = mDialerConnection.connect(null);
689             mIsConnected = result == CONNECTION_SUCCEEDED;
690         }
691 
692         /**
693          * Changes the active {@link InCallService} to a car mode app.  Called whenever the device
694          * changes to car mode or the currently active car mode app changes.
695          *
696          * @param packageName The package name of the car mode app.
697          */
changeCarModeApp(String packageName, UserHandle userHandle)698         public synchronized void changeCarModeApp(String packageName, UserHandle userHandle) {
699             Log.i(this, "changeCarModeApp: isCarModeNow=" + mIsCarMode);
700 
701             InCallServiceInfo currentConnectionInfo = mCurrentConnection == null ? null
702                     : mCurrentConnection.getInfo();
703             InCallServiceInfo carModeConnectionInfo =
704                     getInCallServiceComponent(userHandle, packageName,
705                             IN_CALL_SERVICE_TYPE_CAR_MODE_UI, true /* ignoreDisabed */);
706 
707             if (!Objects.equals(currentConnectionInfo, carModeConnectionInfo)
708                     && carModeConnectionInfo != null) {
709                 Log.i(this, "changeCarModeApp: " + currentConnectionInfo + " => "
710                         + carModeConnectionInfo);
711                 if (mIsConnected) {
712                     mCurrentConnection.disconnect();
713                 }
714 
715                 mCarModeConnection = mCurrentConnection =
716                         new InCallServiceBindingConnection(carModeConnectionInfo, userHandle);
717                 mIsCarMode = true;
718 
719                 int result = mCurrentConnection.connect(null);
720                 mIsConnected = result == CONNECTION_SUCCEEDED;
721             } else {
722                 Log.i(this, "changeCarModeApp: unchanged; " + currentConnectionInfo + " => "
723                         + carModeConnectionInfo);
724             }
725         }
726 
isCarMode()727         public boolean isCarMode() {
728             return mIsCarMode;
729         }
730 
731         @Override
connect(Call call)732         public int connect(Call call) {
733             if (mIsConnected) {
734                 Log.i(this, "already connected");
735                 return CONNECTION_SUCCEEDED;
736             } else {
737                 int result = mCurrentConnection.connect(call);
738                 if (result != CONNECTION_FAILED) {
739                     mIsConnected = result == CONNECTION_SUCCEEDED;
740                     return result;
741                 }
742             }
743 
744             return CONNECTION_FAILED;
745         }
746 
747         @Override
disconnect()748         public void disconnect() {
749             if (mIsConnected) {
750                 Log.i(InCallController.this, "CSICSC: disconnect %s", mCurrentConnection);
751                 mCurrentConnection.disconnect();
752                 mIsConnected = false;
753             } else {
754                 Log.i(this, "already disconnected");
755             }
756         }
757 
758         @Override
isConnected()759         public boolean isConnected() {
760             return mIsConnected;
761         }
762 
763         @Override
setHasEmergency(boolean hasEmergency)764         public void setHasEmergency(boolean hasEmergency) {
765             if (mDialerConnection != null) {
766                 mDialerConnection.setHasEmergency(hasEmergency);
767             }
768             if (mCarModeConnection != null) {
769                 mCarModeConnection.setHasEmergency(hasEmergency);
770             }
771         }
772 
773         @Override
getInfo()774         public InCallServiceInfo getInfo() {
775             return mCurrentConnection.getInfo();
776         }
777 
778         @Override
dump(IndentingPrintWriter pw)779         public void dump(IndentingPrintWriter pw) {
780             pw.print("Car Swapping ICS [");
781             pw.append(mIsConnected ? "" : "not ").append("connected]\n");
782             pw.increaseIndent();
783             if (mDialerConnection != null) {
784                 pw.print("Dialer: ");
785                 mDialerConnection.dump(pw);
786             }
787             if (mCarModeConnection != null) {
788                 pw.print("Car Mode: ");
789                 mCarModeConnection.dump(pw);
790             }
791         }
792 
getCurrentConnection()793         private InCallServiceConnection getCurrentConnection() {
794             if (mIsCarMode && mCarModeConnection != null) {
795                 return mCarModeConnection;
796             } else {
797                 return mDialerConnection;
798             }
799         }
800     }
801 
802     private class NonUIInCallServiceConnectionCollection extends InCallServiceConnection {
803         private final List<InCallServiceBindingConnection> mSubConnections;
804 
NonUIInCallServiceConnectionCollection( List<InCallServiceBindingConnection> subConnections)805         public NonUIInCallServiceConnectionCollection(
806                 List<InCallServiceBindingConnection> subConnections) {
807             mSubConnections = subConnections;
808         }
809 
810         @Override
connect(Call call)811         public int connect(Call call) {
812             for (InCallServiceBindingConnection subConnection : mSubConnections) {
813                 subConnection.connect(call);
814             }
815             return CONNECTION_SUCCEEDED;
816         }
817 
818         @Override
disconnect()819         public void disconnect() {
820             for (InCallServiceBindingConnection subConnection : mSubConnections) {
821                 if (subConnection.isConnected()) {
822                     subConnection.disconnect();
823                 }
824             }
825         }
826 
827         @Override
isConnected()828         public boolean isConnected() {
829             boolean connected = false;
830             for (InCallServiceBindingConnection subConnection : mSubConnections) {
831                 connected = connected || subConnection.isConnected();
832             }
833             return connected;
834         }
835 
836         @Override
dump(IndentingPrintWriter pw)837         public void dump(IndentingPrintWriter pw) {
838             pw.println("Non-UI Connections:");
839             pw.increaseIndent();
840             for (InCallServiceBindingConnection subConnection : mSubConnections) {
841                 subConnection.dump(pw);
842             }
843             pw.decreaseIndent();
844         }
845 
addConnections(List<InCallServiceBindingConnection> newConnections)846         public void addConnections(List<InCallServiceBindingConnection> newConnections) {
847             // connect() needs to be called with a Call object. Since we're in the middle of any
848             // possible number of calls right now, choose an arbitrary one from the ones that
849             // InCallController is tracking.
850             if (mCallIdMapper.getCalls().isEmpty()) {
851                 Log.w(InCallController.this, "No calls tracked while adding new NonUi incall");
852                 return;
853             }
854             Call callToConnectWith = mCallIdMapper.getCalls().iterator().next();
855             for (InCallServiceBindingConnection newConnection : newConnections) {
856                 // Ensure we track the new sub-connection so that when we later disconnect we will
857                 // be able to disconnect it.
858                 mSubConnections.add(newConnection);
859                 newConnection.connect(callToConnectWith);
860             }
861         }
862 
getSubConnections()863         public List<InCallServiceBindingConnection> getSubConnections() {
864             return mSubConnections;
865         }
866     }
867 
868     private final Call.Listener mCallListener = new Call.ListenerBase() {
869         @Override
870         public void onConnectionCapabilitiesChanged(Call call) {
871             updateCall(call);
872         }
873 
874         @Override
875         public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {
876             updateCall(call, false /* includeVideoProvider */, didRttChange, null);
877         }
878 
879         @Override
880         public void onCannedSmsResponsesLoaded(Call call) {
881             updateCall(call);
882         }
883 
884         @Override
885         public void onVideoCallProviderChanged(Call call) {
886             updateCall(call, true /* videoProviderChanged */, false, null);
887         }
888 
889         @Override
890         public void onStatusHintsChanged(Call call) {
891             updateCall(call);
892         }
893 
894         @Override
895         public void onCallerInfoChanged(Call call) {
896             updateCall(call);
897         }
898 
899         /**
900          * Listens for changes to extras reported by a Telecom {@link Call}.
901          *
902          * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
903          * so we will only trigger an update of the call information if the source of the
904          * extras change was a {@link ConnectionService}.
905          *
906          * @param call   The call.
907          * @param source The source of the extras change
908          *               ({@link Call#SOURCE_CONNECTION_SERVICE} or
909          *               {@link Call#SOURCE_INCALL_SERVICE}).
910          * @param extras The extras.
911          */
912         @Override
913         public void onExtrasChanged(Call call, int source, Bundle extras,
914                 String requestingPackageName) {
915             if (source == Call.SOURCE_CONNECTION_SERVICE) {
916                 updateCall(call);
917             } else if (source == Call.SOURCE_INCALL_SERVICE && requestingPackageName != null) {
918                 // If the change originated from another InCallService, we'll propagate the change
919                 // to all other InCallServices running, EXCEPT the one who made the original change.
920                 updateCall(call, false /* videoProviderChanged */, false /* rttInfoChanged */,
921                         requestingPackageName);
922             }
923         }
924 
925         /**
926          * Listens for changes to extras reported by a Telecom {@link Call}.
927          *
928          * Extras changes can originate from a {@link ConnectionService} or an {@link InCallService}
929          * so we will only trigger an update of the call information if the source of the extras
930          * change was a {@link ConnectionService}.
931          *  @param call The call.
932          * @param source The source of the extras change ({@link Call#SOURCE_CONNECTION_SERVICE} or
933          *               {@link Call#SOURCE_INCALL_SERVICE}).
934          * @param keys The extra key removed
935          */
936         @Override
937         public void onExtrasRemoved(Call call, int source, List<String> keys) {
938             // Do not inform InCallServices of changes which originated there.
939             if (source == Call.SOURCE_INCALL_SERVICE) {
940                 return;
941             }
942             updateCall(call);
943         }
944 
945         @Override
946         public void onHandleChanged(Call call) {
947             updateCall(call);
948         }
949 
950         @Override
951         public void onCallerDisplayNameChanged(Call call) {
952             updateCall(call);
953         }
954 
955         @Override
956         public void onCallDirectionChanged(Call call) {
957             updateCall(call);
958         }
959 
960         @Override
961         public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
962             updateCall(call);
963         }
964 
965         @Override
966         public void onTargetPhoneAccountChanged(Call call) {
967             updateCall(call);
968         }
969 
970         @Override
971         public void onConferenceableCallsChanged(Call call) {
972             updateCall(call);
973         }
974 
975         @Override
976         public void onConnectionEvent(Call call, String event, Bundle extras) {
977             notifyConnectionEvent(call, event, extras);
978         }
979 
980         @Override
981         public void onHandoverFailed(Call call, int error) {
982             notifyHandoverFailed(call, error);
983         }
984 
985         @Override
986         public void onHandoverComplete(Call call) {
987             notifyHandoverComplete(call);
988         }
989 
990         @Override
991         public void onRttInitiationFailure(Call call, int reason) {
992             notifyRttInitiationFailure(call, reason);
993             updateCall(call, false, true, null);
994         }
995 
996         @Override
997         public void onRemoteRttRequest(Call call, int requestId) {
998             notifyRemoteRttRequest(call, requestId);
999         }
1000 
1001         @Override
1002         public void onCallerNumberVerificationStatusChanged(Call call,
1003                 int callerNumberVerificationStatus) {
1004             updateCall(call);
1005         }
1006     };
1007 
findChildManagedProfileUser(UserHandle parent, UserManager um)1008     private UserHandle findChildManagedProfileUser(UserHandle parent, UserManager um) {
1009         UserHandle childManagedProfileUser = null;
1010 
1011         //find child managed profile user (if any)
1012         List<UserHandle> allUsers = um.getAllProfiles();
1013         for (UserHandle u : allUsers) {
1014             if ((um.getProfileParent(u) != null) && (um.getProfileParent(u).equals(parent))
1015                     && um.isManagedProfile(u.getIdentifier())) {
1016                 //found managed profile child
1017                 Log.i(this,
1018                         "Child managed profile user found: " + u.getIdentifier());
1019                 childManagedProfileUser = u;
1020                 break;
1021             }
1022         }
1023         return childManagedProfileUser;
1024     }
1025     private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
1026         private List<InCallController.InCallServiceInfo> getNonUiInCallServiceInfoList(
1027                 Intent intent, UserHandle userHandle) {
1028             String changedPackage = intent.getData().getSchemeSpecificPart();
1029             List<InCallController.InCallServiceInfo> inCallServiceInfoList =
1030                     Arrays.stream(intent.getStringArrayExtra(
1031                                     Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))
1032                             .map((className) ->
1033                                     ComponentName.createRelative(changedPackage,
1034                                             className))
1035                             .filter(mKnownNonUiInCallServices::contains)
1036                             .flatMap(componentName -> getInCallServiceComponents(
1037                                     userHandle, componentName,
1038                                     IN_CALL_SERVICE_TYPE_NON_UI).stream())
1039                             .collect(Collectors.toList());
1040             return ((inCallServiceInfoList != null) ? inCallServiceInfoList : new ArrayList<>());
1041         }
1042 
1043         //Here we query components using the userHandle. We then also query components using the
1044         //parent userHandle (if any) while removing duplicates. For non-dup components found using
1045         //parent userHandle, we use the overloaded InCallServiceBindingConnection constructor.
1046         @SuppressWarnings("ReturnValueIgnored")
1047         private List<InCallServiceBindingConnection> getNonUiInCallServiceBindingConnectionList(
1048                 Intent intent, @NonNull UserHandle userHandle, UserHandle parentUserHandle) {
1049             List<InCallServiceBindingConnection> result = new ArrayList<>();
1050             List<InCallController.InCallServiceInfo> serviceInfoListForParent = new ArrayList<>();
1051 
1052             //query and add components for the child
1053             List<InCallController.InCallServiceInfo> serviceInfoListForUser =
1054                     getNonUiInCallServiceInfoList(intent, userHandle);
1055 
1056             //if user has a parent, get components for parents
1057             if (parentUserHandle != null) {
1058                 serviceInfoListForParent = getNonUiInCallServiceInfoList(intent, parentUserHandle);
1059             }
1060 
1061             serviceInfoListForUser
1062                     .stream()
1063                     .map(InCallServiceBindingConnection::new)
1064                     .collect(Collectors.toCollection(() -> result));
1065 
1066             serviceInfoListForParent
1067                     .stream()
1068                     .filter((e) -> !(serviceInfoListForUser.contains(e)))
1069                     .map((serviceinfo) -> new InCallServiceBindingConnection(serviceinfo,
1070                             parentUserHandle))
1071                     .collect(Collectors.toCollection(() -> result));
1072 
1073             return result;
1074         }
1075 
1076         @Override
1077         public void onReceive(Context context, Intent intent) {
1078             Log.startSession("ICC.pCR");
1079             UserManager um = mContext.getSystemService(UserManager.class);
1080             try {
1081                 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
1082                     synchronized (mLock) {
1083                         int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
1084                         String changedPackage = intent.getData().getSchemeSpecificPart();
1085                         UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
1086                         boolean isManagedProfile = um.isManagedProfile(userHandle.getIdentifier());
1087 
1088                         /*
1089                         There are two possibilities here:
1090                          1) We get a work-profile/managed userHandle. In this case we need to check
1091                          if there are any ongoing calls for that user. If yes, then process further
1092                          by querying component using this user handle (also bindAsUser using this
1093                           handle). Else safely ignore it.
1094                                 OR
1095                          2) We get the primary/non-managed userHandle. In this case, we have two
1096                           sub-cases to handle:
1097                                    a) If there are ongoing calls for this user, query components
1098                                    using this user and addConnections
1099                                    b) If there are ongoing calls for the child of this user, we
1100                                    also addConnections to that child (but invoke bindAsUser later
1101                                     with the parent handle).
1102 
1103                          */
1104 
1105                         UserHandle childManagedProfileUser = findChildManagedProfileUser(
1106                                 userHandle, um);
1107                         boolean isUserKeyPresent = mNonUIInCallServiceConnections.containsKey(
1108                                 userHandle);
1109                         boolean isChildUserKeyPresent = (childManagedProfileUser == null) ? false
1110                                 : mNonUIInCallServiceConnections.containsKey(
1111                                         childManagedProfileUser);
1112                         List<InCallServiceBindingConnection> componentsToBindForUser = null;
1113                         List<InCallServiceBindingConnection> componentsToBindForChild = null;
1114                         // Separate binding for BT logic.
1115                         boolean isBluetoothPkg = isBluetoothPackage(changedPackage);
1116                         Call callToConnectWith = mCallIdMapper.getCalls().isEmpty()
1117                                 ? null
1118                                 : mCallIdMapper.getCalls().iterator().next();
1119 
1120                         // Bind to BT service if there's an available call. When the flag isn't
1121                         // enabled, the service will be included as part of
1122                         // getNonUiInCallServiceBindingConnectionList.
1123                         if (mFeatureFlags.separatelyBindToBtIncallService()
1124                                 && isBluetoothPkg && callToConnectWith != null) {
1125                             // mNonUIInCallServiceConnections will always contain a key for
1126                             // userHandle and/or the child user if there is an ongoing call with
1127                             // that user, regardless if there aren't any non-UI ICS bound.
1128                             if (isUserKeyPresent) {
1129                                 bindToBTService(callToConnectWith, userHandle);
1130                             }
1131                             if (isChildUserKeyPresent) {
1132                                 // This will try to use the ICS found in the parent if one isn't
1133                                 // available for the child.
1134                                 bindToBTService(callToConnectWith, childManagedProfileUser);
1135                             }
1136                         }
1137 
1138                         if(isUserKeyPresent) {
1139                             componentsToBindForUser =
1140                                     getNonUiInCallServiceBindingConnectionList(intent,
1141                                             userHandle, null);
1142                         }
1143 
1144                         if (isChildUserKeyPresent) {
1145                             componentsToBindForChild =
1146                                     getNonUiInCallServiceBindingConnectionList(intent,
1147                                             childManagedProfileUser, userHandle);
1148                         }
1149 
1150                         Log.i(this,
1151                                 "isUserKeyPresent:%b isChildKeyPresent:%b isManagedProfile:%b "
1152                                         + "user:%d",
1153                                 isUserKeyPresent, isChildUserKeyPresent, isManagedProfile,
1154                                 userHandle.getIdentifier());
1155 
1156                         if (isUserKeyPresent && !componentsToBindForUser.isEmpty()) {
1157                             mNonUIInCallServiceConnections.get(userHandle).
1158                                     addConnections(componentsToBindForUser);
1159                         }
1160                         if (isChildUserKeyPresent && !componentsToBindForChild.isEmpty()) {
1161                             mNonUIInCallServiceConnections.get(childManagedProfileUser).
1162                                     addConnections(componentsToBindForChild);
1163                         }
1164                         // If the current car mode app become enabled from disabled, update
1165                         // the connection to binding
1166                         updateCarModeForConnections();
1167                     }
1168                 }
1169             } finally {
1170                 Log.endSession();
1171             }
1172         }
1173     };
1174 
1175     private final BroadcastReceiver mUserAddedReceiver = new BroadcastReceiver() {
1176         @Override
1177         public void onReceive(Context context, Intent intent) {
1178             if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
1179                 restrictPhoneCallOps();
1180             }
1181         }
1182     };
1183 
1184     private final SystemStateListener mSystemStateListener = new SystemStateListener() {
1185         @Override
1186         public void onCarModeChanged(int priority, String packageName, boolean isCarMode) {
1187             InCallController.this.handleCarModeChange(priority, packageName, isCarMode);
1188         }
1189 
1190         @Override
1191         public void onAutomotiveProjectionStateSet(String automotiveProjectionPackage) {
1192             InCallController.this.handleSetAutomotiveProjection(automotiveProjectionPackage);
1193         }
1194 
1195         @Override
1196         public void onAutomotiveProjectionStateReleased() {
1197             InCallController.this.handleReleaseAutomotiveProjection();
1198         }
1199 
1200         @Override
1201         public void onPackageUninstalled(String packageName) {
1202             mCarModeTracker.forceRemove(packageName);
1203             updateCarModeForConnections();
1204         }
1205     };
1206 
1207     private static final int IN_CALL_SERVICE_TYPE_INVALID = 0;
1208     private static final int IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI = 1;
1209     private static final int IN_CALL_SERVICE_TYPE_SYSTEM_UI = 2;
1210     private static final int IN_CALL_SERVICE_TYPE_CAR_MODE_UI = 3;
1211     private static final int IN_CALL_SERVICE_TYPE_NON_UI = 4;
1212     private static final int IN_CALL_SERVICE_TYPE_COMPANION = 5;
1213     private static final int IN_CALL_SERVICE_TYPE_BLUETOOTH = 6;
1214 
1215     // Timeout value to be used to ensure future completion for mDisconnectedToneBtFutures. This is
1216     // set to 4 seconds to account for the exceptional case (TONE_CONGESTION).
1217     private static final int DISCONNECTED_TONE_TIMEOUT = 4000;
1218 
1219     private static final int[] LIVE_CALL_STATES = { CallState.ACTIVE, CallState.PULLING,
1220             CallState.DISCONNECTING };
1221 
1222     /** The in-call app implementations, see {@link IInCallService}. */
1223     private final Map<UserHandle, Map<InCallServiceInfo, IInCallService>>
1224             mInCallServices = new ArrayMap<>();
1225     private final Map<UserHandle, Pair<InCallServiceInfo, IInCallService>> mBTInCallServices =
1226             new ArrayMap<>();
1227     private final Map<UserHandle, Map<InCallServiceInfo, IInCallService>>
1228             mCombinedInCallServiceMap = new ArrayMap<>();
1229 
1230     private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
1231     private final Collection<Call> mPendingEndToneCall = new ArraySet<>();
1232 
1233     private final Context mContext;
1234     private final AppOpsManager mAppOpsManager;
1235     private final SensorPrivacyManager mSensorPrivacyManager;
1236     private final TelecomSystem.SyncRoot mLock;
1237     private final CallsManager mCallsManager;
1238     private final SystemStateHelper mSystemStateHelper;
1239     private final Timeouts.Adapter mTimeoutsAdapter;
1240     private final DefaultDialerCache mDefaultDialerCache;
1241     private final EmergencyCallHelper mEmergencyCallHelper;
1242     private final Handler mHandler = new Handler(Looper.getMainLooper());
1243     private final Map<UserHandle, CarSwappingInCallServiceConnection>
1244             mInCallServiceConnections = new ArrayMap<>();
1245     private final Map<UserHandle, NonUIInCallServiceConnectionCollection>
1246             mNonUIInCallServiceConnections = new ArrayMap<>();
1247     private final Map<UserHandle, InCallServiceConnection> mBTInCallServiceConnections =
1248             new ArrayMap<>();
1249     private final ClockProxy mClockProxy;
1250     private final IBinder mToken = new Binder();
1251     private final FeatureFlags mFeatureFlags;
1252 
1253     // A set of known non-UI in call services on the device, including those that are disabled.
1254     // We track this so that we can efficiently bind to them when we're notified that a new
1255     // component has been enabled.
1256     private Set<ComponentName> mKnownNonUiInCallServices = new ArraySet<>();
1257 
1258     // Future that's in a completed state unless we're in the middle of binding to a service.
1259     // The future will complete with true if binding succeeds, false if it timed out.
1260     private CompletableFuture<Boolean> mBindingFuture = CompletableFuture.completedFuture(true);
1261 
1262     // Future that's in a completed state unless we're in the middle of a binding to a bluetooth
1263     // in-call service.
1264     // The future will complete with true if bluetooth in-call service succeeds, false if it timed
1265     // out.
1266     private Map<UserHandle, CompletableFuture<Boolean>> mBtBindingFuture = new ArrayMap<>();
1267     // Future used to delay terminating the BT InCallService before the call disconnect tone
1268     // finishes playing.
1269     private Map<String, CompletableFuture<Void>> mDisconnectedToneBtFutures = new ArrayMap<>();
1270 
1271     private final CarModeTracker mCarModeTracker;
1272 
1273     /**
1274      * The package name of the app which is showing the calling UX.
1275      */
1276     private String mCurrentUserInterfacePackageName = null;
1277 
1278     /**
1279      * {@code true} if InCallController is tracking a managed, not external call which is using the
1280      * microphone, and is not muted {@code false} otherwise.
1281      */
1282     private boolean mIsCallUsingMicrophone = false;
1283 
1284     /**
1285      * {@code true} if InCallController is tracking a managed, not external call which is using the
1286      * microphone, {@code false} otherwise.
1287      */
1288     private boolean mIsTrackingManagedAliveCall = false;
1289 
1290     private boolean mIsStartCallDelayScheduled = false;
1291 
1292     private boolean mDisconnectedToneStartedPlaying = false;
1293 
1294     /**
1295      * A list of call IDs which are currently using the camera.
1296      */
1297     private ArrayList<String> mCallsUsingCamera = new ArrayList<>();
1298 
1299     private ArraySet<String> mAllCarrierPrivilegedApps = new ArraySet<>();
1300     private ArraySet<String> mActiveCarrierPrivilegedApps = new ArraySet<>();
1301 
InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, CarModeTracker carModeTracker, ClockProxy clockProxy, FeatureFlags featureFlags)1302     public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
1303             SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache,
1304             Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper,
1305             CarModeTracker carModeTracker, ClockProxy clockProxy, FeatureFlags featureFlags) {
1306       this(context, lock, callsManager, systemStateHelper, defaultDialerCache, timeoutsAdapter,
1307               emergencyCallHelper, carModeTracker, clockProxy, featureFlags, null);
1308     }
1309 
1310     @VisibleForTesting
InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper, CarModeTracker carModeTracker, ClockProxy clockProxy, FeatureFlags featureFlags, com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags)1311     public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
1312             SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache,
1313             Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper,
1314             CarModeTracker carModeTracker, ClockProxy clockProxy, FeatureFlags featureFlags,
1315             com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags) {
1316         mContext = context;
1317         mAppOpsManager = context.getSystemService(AppOpsManager.class);
1318         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
1319         mLock = lock;
1320         mCallsManager = callsManager;
1321         mSystemStateHelper = systemStateHelper;
1322         mTimeoutsAdapter = timeoutsAdapter;
1323         mDefaultDialerCache = defaultDialerCache;
1324         mEmergencyCallHelper = emergencyCallHelper;
1325         mCarModeTracker = carModeTracker;
1326         mSystemStateHelper.addListener(mSystemStateListener);
1327         mClockProxy = clockProxy;
1328         restrictPhoneCallOps();
1329         IntentFilter userAddedFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
1330         userAddedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
1331         mContext.registerReceiver(mUserAddedReceiver, userAddedFilter);
1332         mFeatureFlags = featureFlags;
1333     }
1334 
restrictPhoneCallOps()1335     private void restrictPhoneCallOps() {
1336         PackageTagsList packageRestriction = new PackageTagsList.Builder()
1337                 .add(mContext.getPackageName())
1338                 .build();
1339         mAppOpsManager.setUserRestrictionForUser(AppOpsManager.OP_PHONE_CALL_MICROPHONE, true,
1340                 mToken, packageRestriction, UserHandle.USER_ALL);
1341         mAppOpsManager.setUserRestrictionForUser(AppOpsManager.OP_PHONE_CALL_CAMERA, true,
1342                 mToken, packageRestriction, UserHandle.USER_ALL);
1343     }
1344 
1345     @Override
onOpActiveChanged(@ndroidx.annotation.NonNull String op, int uid, @androidx.annotation.NonNull String packageName, boolean active)1346     public void onOpActiveChanged(@androidx.annotation.NonNull String op, int uid,
1347             @androidx.annotation.NonNull String packageName, boolean active) {
1348         synchronized (mLock) {
1349             if (!mAllCarrierPrivilegedApps.contains(packageName)) {
1350                 return;
1351             }
1352 
1353             if (active) {
1354                 mActiveCarrierPrivilegedApps.add(packageName);
1355             } else {
1356                 mActiveCarrierPrivilegedApps.remove(packageName);
1357             }
1358             maybeTrackMicrophoneUse(isMuted());
1359         }
1360     }
1361 
updateAllCarrierPrivilegedUsingMic()1362     private void updateAllCarrierPrivilegedUsingMic() {
1363         mActiveCarrierPrivilegedApps.clear();
1364         UserManager userManager = mContext.getSystemService(UserManager.class);
1365         PackageManager pkgManager = mContext.getPackageManager();
1366         for (String pkg : mAllCarrierPrivilegedApps) {
1367             boolean isActive = mActiveCarrierPrivilegedApps.contains(pkg);
1368             List<UserHandle> users = userManager.getUserHandles(true);
1369             for (UserHandle user : users) {
1370                 if (isActive) {
1371                     break;
1372                 }
1373 
1374                 int uid;
1375                 try {
1376                     uid = pkgManager.getPackageUidAsUser(pkg, user.getIdentifier());
1377                 } catch (PackageManager.NameNotFoundException e) {
1378                     continue;
1379                 }
1380                 List<AppOpsManager.PackageOps> pkgOps = mAppOpsManager.getOpsForPackage(
1381                         uid, pkg, OPSTR_RECORD_AUDIO);
1382                 for (int j = 0; j < pkgOps.size(); j++) {
1383                     List<AppOpsManager.OpEntry> opEntries = pkgOps.get(j).getOps();
1384                     for (int k = 0; k < opEntries.size(); k++) {
1385                         AppOpsManager.OpEntry entry = opEntries.get(k);
1386                         if (entry.isRunning()) {
1387                             mActiveCarrierPrivilegedApps.add(pkg);
1388                             break;
1389                         }
1390                     }
1391                 }
1392             }
1393         }
1394     }
1395 
updateAllCarrierPrivileged()1396     private void updateAllCarrierPrivileged() {
1397         mAllCarrierPrivilegedApps.clear();
1398         for (Call call : mCallIdMapper.getCalls()) {
1399             mAllCarrierPrivilegedApps.add(call.getConnectionManagerPhoneAccount()
1400                     .getComponentName().getPackageName());
1401         }
1402     }
1403 
1404     @Override
onCallAdded(Call call)1405     public void onCallAdded(Call call) {
1406         UserHandle userFromCall = getUserFromCall(call);
1407 
1408         Log.i(this, "onCallAdded: %s", call);
1409         // Track the call if we don't already know about it.
1410         addCall(call);
1411 
1412         if (mFeatureFlags.separatelyBindToBtIncallService()) {
1413             boolean bindingToBtRequired = false;
1414             boolean bindingToOtherServicesRequired = false;
1415             if (!isBoundAndConnectedToBTService(userFromCall)) {
1416                 Log.i(this, "onCallAdded: %s; not bound or connected to BT ICS.", call);
1417                 bindingToBtRequired = true;
1418                 bindToBTService(call, null);
1419             }
1420             if (!isBoundAndConnectedToServices(userFromCall)) {
1421                 Log.i(this, "onCallAdded: %s; not bound or connected to other ICS.", call);
1422                 // We are not bound, or we're not connected.
1423                 bindingToOtherServicesRequired = true;
1424                 bindToServices(call);
1425             }
1426             // If either BT service are already bound or other services are already bound, attempt
1427             // to add the new call to the connected incall services.
1428             if (!bindingToBtRequired || !bindingToOtherServicesRequired) {
1429                 addCallToConnectedServices(call, userFromCall);
1430             }
1431         } else {
1432             if (!isBoundAndConnectedToServices(userFromCall)) {
1433                 Log.i(this, "onCallAdded: %s; not bound or connected.", call);
1434                 // We are not bound, or we're not connected.
1435                 bindToServices(call);
1436             } else {
1437                 addCallToConnectedServices(call, userFromCall);
1438             }
1439         }
1440     }
1441 
addCallToConnectedServices(Call call, UserHandle userFromCall)1442     private void addCallToConnectedServices(Call call, UserHandle userFromCall) {
1443         InCallServiceConnection inCallServiceConnection =
1444                 mInCallServiceConnections.get(userFromCall);
1445 
1446         // We are bound, and we are connected.
1447         adjustServiceBindingsForEmergency(userFromCall);
1448 
1449         // This is in case an emergency call is added while there is an existing call.
1450         mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call, userFromCall);
1451 
1452         if (inCallServiceConnection != null) {
1453             Log.i(this, "mInCallServiceConnection isConnected=%b",
1454                     inCallServiceConnection.isConnected());
1455         }
1456 
1457         List<ComponentName> componentsUpdated = new ArrayList<>();
1458         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1459                 getCombinedInCallServiceMap();
1460         if (serviceMap.containsKey(userFromCall)) {
1461             for (Map.Entry<InCallServiceInfo, IInCallService> entry :
1462                     serviceMap.get(userFromCall).entrySet()) {
1463                 InCallServiceInfo info = entry.getKey();
1464 
1465                 if (call.isExternalCall() && !info.isExternalCallsSupported()) {
1466                     continue;
1467                 }
1468 
1469                 if (call.isSelfManaged() && (!call.visibleToInCallService()
1470                         || !info.isSelfManagedCallsSupported())) {
1471                     continue;
1472                 }
1473 
1474                 // Only send the RTT call if it's a UI in-call service
1475                 boolean includeRttCall = false;
1476                 if (inCallServiceConnection != null) {
1477                     includeRttCall = info.equals(inCallServiceConnection.getInfo());
1478                 }
1479 
1480                 componentsUpdated.add(info.getComponentName());
1481                 IInCallService inCallService = entry.getValue();
1482 
1483                 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
1484                         true /* includeVideoProvider */,
1485                         mCallsManager.getPhoneAccountRegistrar(),
1486                         info.isExternalCallsSupported(), includeRttCall,
1487                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
1488                                 info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
1489                 try {
1490                     inCallService.addCall(
1491                             sanitizeParcelableCallForService(info, parcelableCall));
1492                     updateCallTracking(call, info, true /* isAdd */);
1493                 } catch (RemoteException ignored) {
1494                 }
1495             }
1496             Log.i(this, "Call added to ICS: %s", componentsUpdated);
1497         }
1498     }
1499 
1500     @Override
onCallRemoved(Call call)1501     public void onCallRemoved(Call call) {
1502         Log.i(this, "onCallRemoved: %s", call);
1503         // Instead of checking if there are no active calls, we should check if there any calls with
1504         // the same associated user returned from getUserFromCall. For instance, it's possible to
1505         // have calls coexist on the personal profile and work profile, in which case, we would only
1506         // remove the ICS connection for the user associated with the call to be disconnected.
1507         UserHandle userFromCall = getUserFromCall(call);
1508         Stream<Call> callsAssociatedWithUserFromCall = mCallsManager.getCalls().stream()
1509                 .filter((c) -> getUserFromCall(c).equals(userFromCall));
1510         boolean isCallCountZero = mFeatureFlags.associatedUserRefactorForWorkProfile()
1511                 ? callsAssociatedWithUserFromCall.count() == 0
1512                 : mCallsManager.getCalls().isEmpty();
1513         if (isCallCountZero) {
1514             /** Let's add a 2 second delay before we send unbind to the services to hopefully
1515              *  give them enough time to process all the pending messages.
1516              */
1517             mHandler.postDelayed(new Runnable("ICC.oCR", mLock) {
1518                 @Override
1519                 public void loggedRun() {
1520                     // Check again to make sure there are no active calls for the associated user.
1521                     Stream<Call> callsAssociatedWithUserFromCall = mCallsManager.getCalls().stream()
1522                             .filter((c) -> getUserFromCall(c).equals(userFromCall));
1523                     boolean isCallCountZero = mFeatureFlags.associatedUserRefactorForWorkProfile()
1524                             ? callsAssociatedWithUserFromCall.count() == 0
1525                             : mCallsManager.getCalls().isEmpty();
1526                     if (isCallCountZero) {
1527                         unbindFromServices(userFromCall);
1528                         mEmergencyCallHelper.maybeRevokeTemporaryLocationPermission();
1529                     }
1530                 }
1531             }.prepare(), mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
1532                     mContext.getContentResolver()));
1533         }
1534         call.removeListener(mCallListener);
1535         mCallIdMapper.removeCall(call);
1536         if (mCallIdMapper.getCalls().isEmpty()) {
1537             mActiveCarrierPrivilegedApps.clear();
1538             mAppOpsManager.stopWatchingActive(this);
1539         }
1540         maybeTrackMicrophoneUse(isMuted());
1541         onSetCamera(call, null);
1542     }
1543 
1544     @Override
onDisconnectedTonePlaying(Call call, boolean isTonePlaying)1545     public void onDisconnectedTonePlaying(Call call, boolean isTonePlaying) {
1546         Log.i(this, "onDisconnectedTonePlaying: %s -> %b", call, isTonePlaying);
1547         if (mFeatureFlags.separatelyBindToBtIncallService()) {
1548             synchronized (mLock) {
1549                 if (isTonePlaying) {
1550                     mDisconnectedToneStartedPlaying = true;
1551                 } else if (mDisconnectedToneStartedPlaying) {
1552                     mDisconnectedToneStartedPlaying = false;
1553                     if (mDisconnectedToneBtFutures.containsKey(call.getId())) {
1554                         Log.i(this, "onDisconnectedTonePlaying: completing BT "
1555                                 + "disconnected tone future");
1556                         mDisconnectedToneBtFutures.get(call.getId()).complete(null);
1557                     }
1558                     mPendingEndToneCall.remove(call);
1559                     if (!mPendingEndToneCall.isEmpty()) {
1560                         return;
1561                     }
1562                     UserHandle userHandle = getUserFromCall(call);
1563                     if (mBTInCallServiceConnections.containsKey(userHandle)) {
1564                         Log.i(this, "onDisconnectedTonePlaying: Unbinding BT service");
1565                         mBTInCallServiceConnections.get(userHandle).disconnect();
1566                         mBTInCallServiceConnections.remove(userHandle);
1567                     }
1568                     // Ensure that BT ICS instance is cleaned up
1569                     if (mBTInCallServices.remove(userHandle) != null) {
1570                         updateCombinedInCallServiceMap(userHandle);
1571                     }
1572                 }
1573             }
1574         }
1575     }
1576 
1577     @Override
onExternalCallChanged(Call call, boolean isExternalCall)1578     public void onExternalCallChanged(Call call, boolean isExternalCall) {
1579         Log.i(this, "onExternalCallChanged: %s -> %b", call, isExternalCall);
1580 
1581         List<ComponentName> componentsUpdated = new ArrayList<>();
1582         UserHandle userFromCall = getUserFromCall(call);
1583         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1584                 getCombinedInCallServiceMap();
1585         if (!isExternalCall && serviceMap.containsKey(userFromCall)) {
1586             // The call was external but it is no longer external.  We must now add it to any
1587             // InCallServices which do not support external calls.
1588             for (Map.Entry<InCallServiceInfo, IInCallService> entry : serviceMap.
1589                     get(userFromCall).entrySet()) {
1590                 InCallServiceInfo info = entry.getKey();
1591 
1592                 if (info.isExternalCallsSupported()) {
1593                     // For InCallServices which support external calls, the call will have already
1594                     // been added to the connection service, so we do not need to add it again.
1595                     continue;
1596                 }
1597 
1598                 if (call.isSelfManaged() && !call.visibleToInCallService()
1599                         && !info.isSelfManagedCallsSupported()) {
1600                     continue;
1601                 }
1602 
1603                 componentsUpdated.add(info.getComponentName());
1604                 IInCallService inCallService = entry.getValue();
1605 
1606                 // Only send the RTT call if it's a UI in-call service
1607                 boolean includeRttCall = info.equals(mInCallServiceConnections.
1608                         get(userFromCall).getInfo());
1609 
1610                 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
1611                         true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
1612                         info.isExternalCallsSupported(), includeRttCall,
1613                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
1614                                 || info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
1615                 try {
1616                     inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
1617                     updateCallTracking(call, info, true /* isAdd */);
1618                 } catch (RemoteException ignored) {
1619                 }
1620             }
1621             Log.i(this, "Previously external call added to components: %s", componentsUpdated);
1622         } else {
1623             // The call was regular but it is now external.  We must now remove it from any
1624             // InCallServices which do not support external calls.
1625             // Remove the call by sending a call update indicating the call was disconnected.
1626             Log.i(this, "Removing external call %s", call);
1627             if (serviceMap.containsKey(userFromCall)) {
1628                 for (Map.Entry<InCallServiceInfo, IInCallService> entry :
1629                         serviceMap.get(userFromCall).entrySet()) {
1630                     InCallServiceInfo info = entry.getKey();
1631                     if (info.isExternalCallsSupported()) {
1632                         // For InCallServices which support external calls, we do not need to remove
1633                         // the call.
1634                         continue;
1635                     }
1636 
1637                     componentsUpdated.add(info.getComponentName());
1638                     IInCallService inCallService = entry.getValue();
1639 
1640                     ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
1641                             call,
1642                             false /* includeVideoProvider */,
1643                             mCallsManager.getPhoneAccountRegistrar(),
1644                             false /* supportsExternalCalls */,
1645                             android.telecom.Call.STATE_DISCONNECTED /* overrideState */,
1646                             false /* includeRttCall */,
1647                             info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
1648                                     || info.getType() == IN_CALL_SERVICE_TYPE_NON_UI
1649                     );
1650 
1651                     try {
1652                         inCallService.updateCall(
1653                                 sanitizeParcelableCallForService(info, parcelableCall));
1654                     } catch (RemoteException ignored) {
1655                     }
1656                 }
1657                 Log.i(this, "External call removed from components: %s", componentsUpdated);
1658             }
1659         }
1660         maybeTrackMicrophoneUse(isMuted());
1661     }
1662 
1663     @Override
onCallStateChanged(Call call, int oldState, int newState)1664     public void onCallStateChanged(Call call, int oldState, int newState) {
1665         Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(),
1666                 CallState.toString(oldState), CallState.toString(newState));
1667         maybeTrackMicrophoneUse(isMuted());
1668         updateCall(call);
1669     }
1670 
1671     @Override
onConnectionServiceChanged( Call call, ConnectionServiceWrapper oldService, ConnectionServiceWrapper newService)1672     public void onConnectionServiceChanged(
1673             Call call,
1674             ConnectionServiceWrapper oldService,
1675             ConnectionServiceWrapper newService) {
1676         updateCall(call);
1677     }
1678 
1679     @Override
onCallAudioStateChanged(CallAudioState oldCallAudioState, CallAudioState newCallAudioState)1680     public void onCallAudioStateChanged(CallAudioState oldCallAudioState,
1681             CallAudioState newCallAudioState) {
1682         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1683                 getCombinedInCallServiceMap();
1684         if (!serviceMap.isEmpty()) {
1685             Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldCallAudioState,
1686                     newCallAudioState);
1687             maybeTrackMicrophoneUse(newCallAudioState.isMuted());
1688             serviceMap.values().forEach(inCallServices -> {
1689                 for (IInCallService inCallService : inCallServices.values()) {
1690                     try {
1691                         inCallService.onCallAudioStateChanged(newCallAudioState);
1692                     } catch (RemoteException ignored) {
1693                     }
1694                 }
1695             });
1696         }
1697     }
1698 
1699     @Override
onCallEndpointChanged(CallEndpoint callEndpoint)1700     public void onCallEndpointChanged(CallEndpoint callEndpoint) {
1701         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1702                 getCombinedInCallServiceMap();
1703         if (!serviceMap.isEmpty()) {
1704             Log.i(this, "Calling onCallEndpointChanged");
1705             serviceMap.values().forEach(inCallServices -> {
1706                 for (IInCallService inCallService : inCallServices.values()) {
1707                     try {
1708                         inCallService.onCallEndpointChanged(callEndpoint);
1709                     } catch (RemoteException ignored) {
1710                         Log.d(this, "Remote exception calling onCallEndpointChanged");
1711                     }
1712                 }
1713             });
1714         }
1715     }
1716 
1717     @Override
onAvailableCallEndpointsChanged(Set<CallEndpoint> availableCallEndpoints)1718     public void onAvailableCallEndpointsChanged(Set<CallEndpoint> availableCallEndpoints) {
1719         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1720                 getCombinedInCallServiceMap();
1721         if (!serviceMap.isEmpty()) {
1722             Log.i(this, "Calling onAvailableCallEndpointsChanged");
1723             List<CallEndpoint> availableEndpoints = new ArrayList<>(availableCallEndpoints);
1724             serviceMap.values().forEach(inCallServices -> {
1725                 for (IInCallService inCallService : inCallServices.values()) {
1726                     try {
1727                         inCallService.onAvailableCallEndpointsChanged(availableEndpoints);
1728                     } catch (RemoteException ignored) {
1729                         Log.d(this, "Remote exception calling onAvailableCallEndpointsChanged");
1730                     }
1731                 }
1732             });
1733         }
1734     }
1735 
1736     @Override
onMuteStateChanged(boolean isMuted)1737     public void onMuteStateChanged(boolean isMuted) {
1738         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1739                 getCombinedInCallServiceMap();
1740         if (!serviceMap.isEmpty()) {
1741             Log.i(this, "Calling onMuteStateChanged");
1742             serviceMap.values().forEach(inCallServices -> {
1743                 for (IInCallService inCallService : inCallServices.values()) {
1744                     try {
1745                         inCallService.onMuteStateChanged(isMuted);
1746                     } catch (RemoteException ignored) {
1747                         Log.d(this, "Remote exception calling onMuteStateChanged");
1748                     }
1749                 }
1750             });
1751         }
1752     }
1753 
1754     @Override
onCanAddCallChanged(boolean canAddCall)1755     public void onCanAddCallChanged(boolean canAddCall) {
1756         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1757                 getCombinedInCallServiceMap();
1758         if (!serviceMap.isEmpty()) {
1759             Log.i(this, "onCanAddCallChanged : %b", canAddCall);
1760             serviceMap.values().forEach(inCallServices -> {
1761                 for (IInCallService inCallService : inCallServices.values()) {
1762                     try {
1763                         inCallService.onCanAddCallChanged(canAddCall);
1764                     } catch (RemoteException ignored) {
1765                     }
1766                 }
1767             });
1768         }
1769     }
1770 
onPostDialWait(Call call, String remaining)1771     void onPostDialWait(Call call, String remaining) {
1772         UserHandle userFromCall = getUserFromCall(call);
1773         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1774                 getCombinedInCallServiceMap();
1775         if (serviceMap.containsKey(userFromCall)) {
1776             Log.i(this, "Calling onPostDialWait, remaining = %s", remaining);
1777             for (IInCallService inCallService: serviceMap.get(userFromCall).values()) {
1778                 try {
1779                     inCallService.setPostDialWait(mCallIdMapper.getCallId(call), remaining);
1780                 } catch (RemoteException ignored) {
1781                 }
1782             }
1783         }
1784     }
1785 
1786     @Override
onIsConferencedChanged(Call call)1787     public void onIsConferencedChanged(Call call) {
1788         Log.d(this, "onIsConferencedChanged %s", call);
1789         updateCall(call);
1790     }
1791 
1792     @Override
onConnectionTimeChanged(Call call)1793     public void onConnectionTimeChanged(Call call) {
1794         Log.d(this, "onConnectionTimeChanged %s", call);
1795         updateCall(call);
1796     }
1797 
1798     @Override
onIsVoipAudioModeChanged(Call call)1799     public void onIsVoipAudioModeChanged(Call call) {
1800         Log.d(this, "onIsVoipAudioModeChanged %s", call);
1801         updateCall(call);
1802         maybeTrackMicrophoneUse(isMuted());
1803     }
1804 
1805     @Override
onConferenceStateChanged(Call call, boolean isConference)1806     public void onConferenceStateChanged(Call call, boolean isConference) {
1807         Log.d(this, "onConferenceStateChanged %s ,isConf=%b", call, isConference);
1808         updateCall(call);
1809     }
1810 
1811     @Override
onCdmaConferenceSwap(Call call)1812     public void onCdmaConferenceSwap(Call call) {
1813         Log.d(this, "onCdmaConferenceSwap %s", call);
1814         updateCall(call);
1815     }
1816 
1817     /**
1818      * Track changes to camera usage for a call.
1819      *
1820      * @param call     The call.
1821      * @param cameraId The id of the camera to use, or {@code null} if camera is off.
1822      */
1823     @Override
onSetCamera(Call call, String cameraId)1824     public void onSetCamera(Call call, String cameraId) {
1825         if (call == null) {
1826             return;
1827         }
1828 
1829         Log.i(this, "onSetCamera callId=%s, cameraId=%s", call.getId(), cameraId);
1830         if (cameraId != null) {
1831             boolean shouldStart = mCallsUsingCamera.isEmpty();
1832             if (!mCallsUsingCamera.contains(call.getId())) {
1833                 mCallsUsingCamera.add(call.getId());
1834             }
1835 
1836             if (shouldStart) {
1837                 // Note, not checking return value, as this op call is merely for tracing use
1838                 mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
1839                         mContext.getOpPackageName(), false, null, null);
1840                 mSensorPrivacyManager.showSensorUseDialog(SensorPrivacyManager.Sensors.CAMERA);
1841             }
1842         } else {
1843             boolean hadCall = !mCallsUsingCamera.isEmpty();
1844             mCallsUsingCamera.remove(call.getId());
1845             if (hadCall && mCallsUsingCamera.isEmpty()) {
1846                 mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
1847                         mContext.getOpPackageName(), null);
1848             }
1849         }
1850     }
1851 
1852     @VisibleForTesting
bringToForeground(boolean showDialpad, UserHandle callingUser)1853     public void bringToForeground(boolean showDialpad, UserHandle callingUser) {
1854         KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
1855         boolean isLockscreenRestricted = keyguardManager != null
1856                 && keyguardManager.isKeyguardLocked();
1857         UserHandle currentUser = mCallsManager.getCurrentUserHandle();
1858         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1859                 getCombinedInCallServiceMap();
1860         // Handle cases when calls are placed from the keyguard UI screen, which operates under
1861         // the admin user. This needs to account for emergency calls placed from secondary/guest
1862         // users as well as the work profile. Once the screen is locked, the user should be able to
1863         // return to the call (from the keyguard UI).
1864         if (mFeatureFlags.eccKeyguard() && mCallsManager.isInEmergencyCall()
1865                 && isLockscreenRestricted && !serviceMap.containsKey(callingUser)) {
1866             // If screen is locked and the current user is the system, query calls for the work
1867             // profile user, if available. Otherwise, the user is in the secondary/guest profile,
1868             // so we can default to the system user.
1869             if (currentUser.isSystem()) {
1870                 UserManager um = mContext.getSystemService(UserManager.class);
1871                 UserHandle workProfileUser = findChildManagedProfileUser(currentUser, um);
1872                 boolean hasWorkCalls = mCallsManager.getCalls().stream()
1873                         .filter((c) -> getUserFromCall(c).equals(workProfileUser)).count() > 0;
1874                 callingUser = hasWorkCalls ? workProfileUser : currentUser;
1875             } else {
1876                 callingUser = currentUser;
1877             }
1878         }
1879         if (serviceMap.containsKey(callingUser)) {
1880             for (IInCallService inCallService : serviceMap.get(callingUser).values()) {
1881                 try {
1882                     inCallService.bringToForeground(showDialpad);
1883                 } catch (RemoteException ignored) {
1884                 }
1885             }
1886         } else {
1887             Log.w(this, "Asking to bring unbound in-call UI to foreground.");
1888         }
1889     }
1890 
1891     @VisibleForTesting
getInCallServices()1892     public Map<UserHandle, Map<InCallServiceInfo, IInCallService>> getInCallServices() {
1893         return getCombinedInCallServiceMap();
1894     }
1895 
1896     @VisibleForTesting
getInCallServiceConnections()1897     public Map<UserHandle, CarSwappingInCallServiceConnection> getInCallServiceConnections() {
1898         return mInCallServiceConnections;
1899     }
1900 
silenceRinger(Set<UserHandle> userHandles)1901     void silenceRinger(Set<UserHandle> userHandles) {
1902         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1903                 getCombinedInCallServiceMap();
1904         userHandles.forEach(userHandle -> {
1905             if (serviceMap.containsKey(userHandle)) {
1906                 for (IInCallService inCallService : serviceMap.get(userHandle).values()) {
1907                     try {
1908                         inCallService.silenceRinger();
1909                     } catch (RemoteException ignored) {
1910                     }
1911                 }
1912             }
1913         });
1914     }
1915 
notifyConnectionEvent(Call call, String event, Bundle extras)1916     private void notifyConnectionEvent(Call call, String event, Bundle extras) {
1917         UserHandle userFromCall = getUserFromCall(call);
1918         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1919                 getCombinedInCallServiceMap();
1920         if (serviceMap.containsKey(userFromCall)) {
1921             for (IInCallService inCallService : serviceMap.get(userFromCall).values()) {
1922                 try {
1923                     Log.i(this, "notifyConnectionEvent {Call: %s, Event: %s, Extras:[%s]}",
1924                             (call != null ? call.toString() : "null"),
1925                             (event != null ? event : "null"),
1926                             (extras != null ? extras.toString() : "null"));
1927                     inCallService.onConnectionEvent(mCallIdMapper.getCallId(call), event, extras);
1928                 } catch (RemoteException ignored) {
1929                 }
1930             }
1931         }
1932     }
1933 
notifyRttInitiationFailure(Call call, int reason)1934     private void notifyRttInitiationFailure(Call call, int reason) {
1935         UserHandle userFromCall = getUserFromCall(call);
1936         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1937                 getCombinedInCallServiceMap();
1938         if (serviceMap.containsKey(userFromCall)) {
1939             serviceMap.get(userFromCall).entrySet().stream()
1940                     .filter((entry) -> entry.getKey().equals(mInCallServiceConnections.
1941                             get(userFromCall).getInfo()))
1942                     .forEach((entry) -> {
1943                         try {
1944                             Log.i(this, "notifyRttFailure, call %s, incall %s",
1945                                     call, entry.getKey());
1946                             entry.getValue().onRttInitiationFailure(mCallIdMapper.getCallId(call),
1947                                     reason);
1948                         } catch (RemoteException ignored) {
1949                         }
1950                     });
1951         }
1952     }
1953 
notifyRemoteRttRequest(Call call, int requestId)1954     private void notifyRemoteRttRequest(Call call, int requestId) {
1955         UserHandle userFromCall = getUserFromCall(call);
1956         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1957                 getCombinedInCallServiceMap();
1958         if (serviceMap.containsKey(userFromCall)) {
1959             serviceMap.get(userFromCall).entrySet().stream()
1960                     .filter((entry) -> entry.getKey().equals(mInCallServiceConnections.
1961                             get(userFromCall).getInfo()))
1962                     .forEach((entry) -> {
1963                         try {
1964                             Log.i(this, "notifyRemoteRttRequest, call %s, incall %s",
1965                                     call, entry.getKey());
1966                             entry.getValue().onRttUpgradeRequest(
1967                                     mCallIdMapper.getCallId(call), requestId);
1968                         } catch (RemoteException ignored) {
1969                         }
1970                     });
1971         }
1972     }
1973 
notifyHandoverFailed(Call call, int error)1974     private void notifyHandoverFailed(Call call, int error) {
1975         UserHandle userFromCall = getUserFromCall(call);
1976         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1977                 getCombinedInCallServiceMap();
1978         if (serviceMap.containsKey(userFromCall)) {
1979             for (IInCallService inCallService : serviceMap.get(userFromCall).values()) {
1980                 try {
1981                     inCallService.onHandoverFailed(mCallIdMapper.getCallId(call), error);
1982                 } catch (RemoteException ignored) {
1983                 }
1984             }
1985         }
1986     }
1987 
notifyHandoverComplete(Call call)1988     private void notifyHandoverComplete(Call call) {
1989         UserHandle userFromCall = getUserFromCall(call);
1990         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
1991                 getCombinedInCallServiceMap();
1992         if (serviceMap.containsKey(userFromCall)) {
1993             for (IInCallService inCallService : serviceMap.get(userFromCall).values()) {
1994                 try {
1995                     inCallService.onHandoverComplete(mCallIdMapper.getCallId(call));
1996                 } catch (RemoteException ignored) {
1997                 }
1998             }
1999         }
2000     }
2001 
2002     /**
2003      * Unbinds an existing bound connection to the in-call app.
2004      */
unbindFromServices(UserHandle userHandle)2005     public void unbindFromServices(UserHandle userHandle) {
2006         Log.i(this, "Unbinding from services for user %s", userHandle);
2007         try {
2008             mContext.unregisterReceiver(mPackageChangedReceiver);
2009         } catch (IllegalArgumentException e) {
2010             // Ignore this -- we may or may not have registered it, but when we bind, we want to
2011             // unregister no matter what.
2012         }
2013         if (mInCallServiceConnections.containsKey(userHandle)) {
2014             mInCallServiceConnections.get(userHandle).disconnect();
2015             mInCallServiceConnections.remove(userHandle);
2016         }
2017         if (mNonUIInCallServiceConnections.containsKey(userHandle)) {
2018             mNonUIInCallServiceConnections.get(userHandle).disconnect();
2019             mNonUIInCallServiceConnections.remove(userHandle);
2020         }
2021         getCombinedInCallServiceMap().remove(userHandle);
2022         if (mFeatureFlags.separatelyBindToBtIncallService()) {
2023             // Note that the BT ICS will be repopulated as part of the combined map if the
2024             // BT ICS is still bound (disconnected tone hasn't finished playing).
2025             updateCombinedInCallServiceMap(userHandle);
2026         }
2027     }
2028 
2029     /**
2030      * Binds to Bluetooth InCallServices. Method-invoker must check
2031      * {@link #isBoundAndConnectedToBTService(UserHandle)} before invoking.
2032      *
2033      * @param call The newly added call that triggered the binding to the in-call services.
2034      */
bindToBTService(Call call, UserHandle userHandle)2035     public void bindToBTService(Call call, UserHandle userHandle) {
2036         Log.i(this, "bindToBtService");
2037         UserHandle userToBind = userHandle == null
2038                 ? getUserFromCall(call)
2039                 : userHandle;
2040         UserManager um = mContext.getSystemService(UserManager.class);
2041         UserHandle parentUser = mFeatureFlags.profileUserSupport()
2042                 ? um.getProfileParent(userToBind) : null;
2043 
2044         if (!mFeatureFlags.profileUserSupport()
2045                 && um.isManagedProfile(userToBind.getIdentifier())) {
2046             parentUser = um.getProfileParent(userToBind);
2047         }
2048 
2049         // Track the call if we don't already know about it.
2050         addCall(call);
2051         List<InCallServiceInfo> infos = getInCallServiceComponents(userToBind,
2052                 IN_CALL_SERVICE_TYPE_BLUETOOTH);
2053         boolean serviceUnavailableForUser = false;
2054         if (infos.size() == 0 || infos.get(0) == null) {
2055             Log.i(this, "No available BT ICS for user (%s). Trying with parent instead.",
2056                     userToBind);
2057             serviceUnavailableForUser = true;
2058             // Check if the service is available under the parent user instead.
2059             if (parentUser != null) {
2060                 infos = getInCallServiceComponents(parentUser, IN_CALL_SERVICE_TYPE_BLUETOOTH);
2061             }
2062             if (infos.size() == 0 || infos.get(0) == null) {
2063                 Log.w(this, "No available BT ICS to bind to for user %s or its parent %s.",
2064                         userToBind, parentUser);
2065                 mBtBindingFuture.put(userToBind, CompletableFuture.completedFuture(false));
2066                 return;
2067             }
2068         }
2069 
2070         mBtBindingFuture.put(userToBind, new CompletableFuture<Boolean>().completeOnTimeout(false,
2071                 mTimeoutsAdapter.getCallBindBluetoothInCallServicesDelay(
2072                         mContext.getContentResolver()), TimeUnit.MILLISECONDS));
2073         InCallServiceBindingConnection btIcsBindingConnection =
2074                 new InCallServiceBindingConnection(infos.get(0),
2075                         serviceUnavailableForUser ? parentUser : userToBind);
2076         mBTInCallServiceConnections.put(userToBind, btIcsBindingConnection);
2077         btIcsBindingConnection.connect(call);
2078     }
2079 
2080     /**
2081      * Binds to all the UI-providing InCallService as well as system-implemented non-UI
2082      * InCallServices. Method-invoker must check {@link #isBoundAndConnectedToServices(UserHandle)}
2083      * before invoking.
2084      *
2085      * @param call           The newly added call that triggered the binding to the in-call
2086      *                      services.
2087      */
2088     @VisibleForTesting
bindToServices(Call call)2089     public void bindToServices(Call call) {
2090         UserHandle userFromCall = getUserFromCall(call);
2091         UserManager um = mContext.getSystemService(UserManager.class);
2092         UserHandle parentUser = mFeatureFlags.profileUserSupport()
2093                 ? um.getProfileParent(userFromCall) : null;
2094         if (!mFeatureFlags.profileUserSupport()
2095                 && um.isManagedProfile(userFromCall.getIdentifier())) {
2096             parentUser = um.getProfileParent(userFromCall);
2097         }
2098         Log.i(this, "child:%s  parent:%s", userFromCall, parentUser);
2099 
2100         if (!mInCallServiceConnections.containsKey(userFromCall)) {
2101             InCallServiceConnection dialerInCall = null;
2102             InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent(userFromCall);
2103             Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
2104             if (defaultDialerComponentInfo != null &&
2105                     !defaultDialerComponentInfo.getComponentName().equals(
2106                             mDefaultDialerCache.getSystemDialerComponent())) {
2107                 dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
2108             }
2109             Log.i(this, "defaultDialer: " + dialerInCall);
2110 
2111             InCallServiceInfo systemInCallInfo = getInCallServiceComponent(userFromCall,
2112                     mDefaultDialerCache.getSystemDialerComponent(), IN_CALL_SERVICE_TYPE_SYSTEM_UI);
2113             EmergencyInCallServiceConnection systemInCall =
2114                     new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
2115             systemInCall.setHasEmergency(mCallsManager.isInEmergencyCall());
2116 
2117             InCallServiceConnection carModeInCall = null;
2118             InCallServiceInfo carModeComponentInfo = getCurrentCarModeComponent(userFromCall);
2119             InCallServiceInfo carModeComponentInfoForParentUser = null;
2120             if(parentUser != null) {
2121                 //query using parent user too
2122                 carModeComponentInfoForParentUser = getCurrentCarModeComponent(
2123                         parentUser);
2124             }
2125 
2126             if (carModeComponentInfo != null &&
2127                     !carModeComponentInfo.getComponentName().equals(
2128                             mDefaultDialerCache.getSystemDialerComponent())) {
2129                 carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
2130             } else if (carModeComponentInfo == null &&
2131                     carModeComponentInfoForParentUser != null &&
2132                     !carModeComponentInfoForParentUser.getComponentName().equals(
2133                             mDefaultDialerCache.getSystemDialerComponent())) {
2134                 carModeInCall = new InCallServiceBindingConnection(
2135                         carModeComponentInfoForParentUser, parentUser);
2136                 Log.i(this, "Using car mode component queried using parent handle");
2137             }
2138 
2139             mInCallServiceConnections.put(userFromCall,
2140                     new CarSwappingInCallServiceConnection(systemInCall, carModeInCall));
2141         }
2142 
2143         CarSwappingInCallServiceConnection inCallServiceConnection =
2144                 mInCallServiceConnections.get(userFromCall);
2145         inCallServiceConnection.chooseInitialInCallService(shouldUseCarModeUI());
2146 
2147         // Actually try binding to the UI InCallService.
2148         if (inCallServiceConnection.connect(call) ==
2149                 InCallServiceConnection.CONNECTION_SUCCEEDED || (call != null
2150                 && call.isSelfManaged())) {
2151             // Only connect to the non-ui InCallServices if we actually connected to the main UI
2152             // one, or if the call is self-managed (in which case we'd still want to keep Wear, BT,
2153             // etc. informed.
2154             connectToNonUiInCallServices(call);
2155             mBindingFuture = new CompletableFuture<Boolean>().completeOnTimeout(false,
2156                     mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
2157                             mContext.getContentResolver()),
2158                     TimeUnit.MILLISECONDS);
2159         } else {
2160             Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
2161         }
2162 
2163         IntentFilter packageChangedFilter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
2164         packageChangedFilter.addDataScheme("package");
2165         mContext.registerReceiverAsUser(mPackageChangedReceiver, UserHandle.ALL,
2166                 packageChangedFilter, null, null);
2167     }
2168 
updateNonUiInCallServices(Call call)2169     private void updateNonUiInCallServices(Call call) {
2170         UserHandle userFromCall = getUserFromCall(call);
2171 
2172         UserManager um = mContext.getSystemService(UserManager.class);
2173         UserHandle parentUser = mFeatureFlags.profileUserSupport()
2174                 ? um.getProfileParent(userFromCall) : null;
2175 
2176         if (!mFeatureFlags.profileUserSupport()
2177                 && um.isManagedProfile(userFromCall.getIdentifier())) {
2178             parentUser = um.getProfileParent(userFromCall);
2179         }
2180 
2181         List<InCallServiceInfo> nonUIInCallComponents =
2182                 getInCallServiceComponents(userFromCall, IN_CALL_SERVICE_TYPE_NON_UI);
2183         List<InCallServiceInfo> nonUIInCallComponentsForParent = new ArrayList<>();
2184         if(parentUser != null) {
2185             //also get Non-UI services using parent handle.
2186             nonUIInCallComponentsForParent =
2187                     getInCallServiceComponents(parentUser, IN_CALL_SERVICE_TYPE_NON_UI);
2188 
2189         }
2190         List<InCallServiceBindingConnection> nonUIInCalls = new LinkedList<>();
2191         for (InCallServiceInfo serviceInfo : nonUIInCallComponents) {
2192             nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo));
2193         }
2194 
2195         //add nonUI InCall services queried using parent user (if any)
2196         for (InCallServiceInfo serviceInfo : nonUIInCallComponentsForParent) {
2197             if (nonUIInCallComponents.contains(serviceInfo)) {
2198                 //skip dups
2199                 Log.i(this, "skipped duplicate component found using parent user: "
2200                         + serviceInfo.getComponentName());
2201             } else {
2202                 nonUIInCalls.add(new InCallServiceBindingConnection(serviceInfo, parentUser));
2203                 Log.i(this,
2204                         "added component queried using parent user: "
2205                                 + serviceInfo.getComponentName());
2206             }
2207         }
2208 
2209         List<String> callCompanionApps = mCallsManager
2210                 .getRoleManagerAdapter().getCallCompanionApps();
2211         if (callCompanionApps != null && !callCompanionApps.isEmpty()) {
2212             for (String pkg : callCompanionApps) {
2213                 InCallServiceInfo info = getInCallServiceComponent(userFromCall, pkg,
2214                         IN_CALL_SERVICE_TYPE_COMPANION, true /* ignoreDisabled */);
2215                 if (info != null) {
2216                     nonUIInCalls.add(new InCallServiceBindingConnection(info));
2217                 }
2218             }
2219         }
2220         mNonUIInCallServiceConnections.put(userFromCall, new NonUIInCallServiceConnectionCollection(
2221                 nonUIInCalls));
2222     }
2223 
connectToNonUiInCallServices(Call call)2224     private void connectToNonUiInCallServices(Call call) {
2225         UserHandle userFromCall = getUserFromCall(call);
2226         if (!mNonUIInCallServiceConnections.containsKey(userFromCall)) {
2227             updateNonUiInCallServices(call);
2228         }
2229         mNonUIInCallServiceConnections.get(userFromCall).connect(call);
2230     }
2231 
getDefaultDialerComponent(UserHandle userHandle)2232     private @Nullable InCallServiceInfo getDefaultDialerComponent(UserHandle userHandle) {
2233         String defaultPhoneAppName = mDefaultDialerCache.getDefaultDialerApplication(
2234                 userHandle.getIdentifier());
2235         String systemPhoneAppName = mDefaultDialerCache.getSystemDialerApplication();
2236 
2237         Log.d(this, "getDefaultDialerComponent: defaultPhoneAppName=[%s]", defaultPhoneAppName);
2238         Log.d(this, "getDefaultDialerComponent: systemPhoneAppName=[%s]", systemPhoneAppName);
2239 
2240         // Get the defaultPhoneApp InCallService component...
2241         InCallServiceInfo defaultPhoneAppComponent =
2242                 (systemPhoneAppName != null && systemPhoneAppName.equals(defaultPhoneAppName)) ?
2243                         /* The defaultPhoneApp is also the systemPhoneApp. Get systemPhoneApp info*/
2244                         getInCallServiceComponent(userHandle, defaultPhoneAppName,
2245                                 IN_CALL_SERVICE_TYPE_SYSTEM_UI, true /* ignoreDisabled */)
2246                         /* The defaultPhoneApp is NOT the systemPhoneApp. Get defaultPhoneApp info*/
2247                         : getInCallServiceComponent(userHandle, defaultPhoneAppName,
2248                                 IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI, true /* ignoreDisabled */);
2249 
2250         Log.d(this, "getDefaultDialerComponent: defaultPhoneAppComponent=[%s]",
2251                 defaultPhoneAppComponent);
2252 
2253         // defaultPhoneAppComponent is null in the case when the defaultPhoneApp does not implement
2254         // the InCallService && is the package is different from the systemPhoneApp
2255 
2256         return defaultPhoneAppComponent;
2257     }
2258 
getCurrentCarModeComponent(UserHandle userHandle)2259     private InCallServiceInfo getCurrentCarModeComponent(UserHandle userHandle) {
2260         return getInCallServiceComponent(userHandle,
2261                 mCarModeTracker.getCurrentCarModePackage(),
2262                 IN_CALL_SERVICE_TYPE_CAR_MODE_UI, true /* ignoreDisabled */);
2263     }
2264 
getInCallServiceComponent(UserHandle userHandle, ComponentName componentName, int type)2265     private InCallServiceInfo getInCallServiceComponent(UserHandle userHandle,
2266             ComponentName componentName, int type) {
2267         List<InCallServiceInfo> list = getInCallServiceComponents(userHandle,
2268                 componentName, type);
2269         if (list != null && !list.isEmpty()) {
2270             return list.get(0);
2271         } else {
2272             // Last Resort: Try to bind to the ComponentName given directly.
2273             Log.e(this, new Exception(), "Package Manager could not find ComponentName: "
2274                     + componentName + ". Trying to bind anyway.");
2275             return new InCallServiceInfo(componentName, false, false, type, false);
2276         }
2277     }
2278 
getInCallServiceComponent(UserHandle userHandle, String packageName, int type, boolean ignoreDisabled)2279     private InCallServiceInfo getInCallServiceComponent(UserHandle userHandle,
2280             String packageName, int type, boolean ignoreDisabled) {
2281         List<InCallServiceInfo> list = getInCallServiceComponents(userHandle,
2282                 packageName, type, ignoreDisabled);
2283         if (list != null && !list.isEmpty()) {
2284             return list.get(0);
2285         }
2286         return null;
2287     }
2288 
getInCallServiceComponents( UserHandle userHandle, int type)2289     private List<InCallServiceInfo> getInCallServiceComponents(
2290             UserHandle userHandle, int type) {
2291         return getInCallServiceComponents(userHandle, null, null, type);
2292     }
2293 
getInCallServiceComponents(UserHandle userHandle, String packageName, int type, boolean ignoreDisabled)2294     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
2295             String packageName, int type, boolean ignoreDisabled) {
2296         return getInCallServiceComponents(userHandle, packageName, null,
2297                 type, ignoreDisabled);
2298     }
2299 
getInCallServiceComponents(UserHandle userHandle, ComponentName componentName, int type)2300     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
2301             ComponentName componentName, int type) {
2302         return getInCallServiceComponents(userHandle, null, componentName, type);
2303     }
2304 
getInCallServiceComponents(UserHandle userHandle, String packageName, ComponentName componentName, int requestedType)2305     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
2306             String packageName, ComponentName componentName, int requestedType) {
2307         return getInCallServiceComponents(userHandle, packageName,
2308                 componentName, requestedType, true /* ignoreDisabled */);
2309     }
canInteractAcrossUsersOrProfiles(ServiceInfo serviceInfo, PackageManager packageManager)2310     private boolean canInteractAcrossUsersOrProfiles(ServiceInfo serviceInfo,
2311             PackageManager packageManager) {
2312         String op = AppOpsManager.permissionToOp("android.permission.INTERACT_ACROSS_PROFILES");
2313         String[] uidPackages = packageManager.getPackagesForUid(serviceInfo.applicationInfo.uid);
2314 
2315         boolean hasInteractAcrossProfiles = Arrays.stream(uidPackages).anyMatch(
2316                 p -> ((packageManager.checkPermission(
2317                         Manifest.permission.INTERACT_ACROSS_PROFILES,
2318                         p) == PackageManager.PERMISSION_GRANTED)
2319                 ));
2320         boolean hasInteractAcrossUsers = Arrays.stream(uidPackages).anyMatch(
2321                 p -> ((packageManager.checkPermission(
2322                         Manifest.permission.INTERACT_ACROSS_USERS,
2323                         p) == PackageManager.PERMISSION_GRANTED)
2324                 ));
2325         boolean hasInteractAcrossProfilesAppOp = Arrays.stream(uidPackages).anyMatch(
2326                 p -> (AppOpsManager.MODE_ALLOWED == mAppOpsManager.checkOpNoThrow(
2327                         op, serviceInfo.applicationInfo.uid, p))
2328         );
2329         Log.i(this,
2330                 "packageName:%s INTERACT_ACROSS_USERS:%b INTERACT_ACROSS_PROFILES:%b "
2331                         + "INTERACT_ACROSS_PROFILES_APPOP:%b",
2332                 uidPackages[0], hasInteractAcrossUsers, hasInteractAcrossProfiles,
2333                 hasInteractAcrossProfilesAppOp);
2334 
2335         return (hasInteractAcrossUsers || hasInteractAcrossProfiles
2336                 || hasInteractAcrossProfilesAppOp);
2337     }
2338 
getInCallServiceComponents(UserHandle userHandle, String packageName, ComponentName componentName, int requestedType, boolean ignoreDisabled)2339     private List<InCallServiceInfo> getInCallServiceComponents(UserHandle userHandle,
2340             String packageName, ComponentName componentName,
2341             int requestedType, boolean ignoreDisabled) {
2342         List<InCallServiceInfo> retval = new LinkedList<>();
2343 
2344         Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);
2345         if (packageName != null) {
2346             serviceIntent.setPackage(packageName);
2347         }
2348         if (componentName != null) {
2349             serviceIntent.setComponent(componentName);
2350         }
2351         Log.i(this,
2352                 "getComponents, pkgname: " + packageName + " comp: " + componentName + " userid: "
2353                         + userHandle.getIdentifier() + " requestedType: " + requestedType);
2354         PackageManager packageManager = mContext.getPackageManager();
2355         Context userContext = mContext.createContextAsUser(userHandle,
2356                 0 /* flags */);
2357         PackageManager userPackageManager = userContext != null ?
2358                 userContext.getPackageManager() : packageManager;
2359 
2360 
2361         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
2362                 serviceIntent,
2363                 PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS,
2364                 userHandle.getIdentifier())) {
2365             ServiceInfo serviceInfo = entry.serviceInfo;
2366 
2367             if (serviceInfo != null) {
2368                 boolean isExternalCallsSupported = serviceInfo.metaData != null &&
2369                         serviceInfo.metaData.getBoolean(
2370                                 TelecomManager.METADATA_INCLUDE_EXTERNAL_CALLS, false);
2371                 boolean isSelfManageCallsSupported = serviceInfo.metaData != null &&
2372                         serviceInfo.metaData.getBoolean(
2373                                 TelecomManager.METADATA_INCLUDE_SELF_MANAGED_CALLS, false);
2374 
2375                 int currentType = getInCallServiceType(userHandle,
2376                         entry.serviceInfo, packageManager, packageName);
2377 
2378                 boolean hasInteractAcrossUserOrProfilePerm = canInteractAcrossUsersOrProfiles(
2379                         entry.serviceInfo, packageManager);
2380 
2381                 ComponentName foundComponentName =
2382                         new ComponentName(serviceInfo.packageName, serviceInfo.name);
2383                 if (currentType == IN_CALL_SERVICE_TYPE_NON_UI) {
2384                     mKnownNonUiInCallServices.add(foundComponentName);
2385                 }
2386 
2387                 boolean isEnabled = isServiceEnabled(foundComponentName,
2388                         serviceInfo, userPackageManager);
2389                 boolean isRequestedType;
2390                 if (requestedType == IN_CALL_SERVICE_TYPE_INVALID) {
2391                     isRequestedType = true;
2392                 } else {
2393                     isRequestedType = requestedType == currentType;
2394                 }
2395 
2396                 Log.i(this,
2397                         "found:%s isRequestedtype:%b isEnabled:%b ignoreDisabled:%b "
2398                                 + "hasCrossProfilePerm:%b",
2399                         foundComponentName, isRequestedType, isEnabled, ignoreDisabled,
2400                         hasInteractAcrossUserOrProfilePerm);
2401 
2402                 if ((!ignoreDisabled || isEnabled) && isRequestedType) {
2403                     retval.add(new InCallServiceInfo(foundComponentName, isExternalCallsSupported,
2404                             isSelfManageCallsSupported, requestedType,
2405                             hasInteractAcrossUserOrProfilePerm));
2406                 }
2407             }
2408         }
2409         return retval;
2410     }
2411 
isServiceEnabled(ComponentName componentName, ServiceInfo serviceInfo, PackageManager packageManager)2412     private boolean isServiceEnabled(ComponentName componentName,
2413             ServiceInfo serviceInfo, PackageManager packageManager) {
2414         if (packageManager == null) {
2415             return serviceInfo.isEnabled();
2416         }
2417 
2418         int componentEnabledState = packageManager.getComponentEnabledSetting(componentName);
2419 
2420         if (componentEnabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
2421             return true;
2422         }
2423 
2424         if (componentEnabledState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
2425             return serviceInfo.isEnabled();
2426         }
2427 
2428         return false;
2429     }
2430 
shouldUseCarModeUI()2431     private boolean shouldUseCarModeUI() {
2432         return mCarModeTracker.isInCarMode();
2433     }
2434 
2435     /**
2436      * Returns the type of InCallService described by the specified serviceInfo.
2437      */
getInCallServiceType(UserHandle userHandle, ServiceInfo serviceInfo, PackageManager packageManager, String packageName)2438     private int getInCallServiceType(UserHandle userHandle, ServiceInfo serviceInfo,
2439             PackageManager packageManager, String packageName) {
2440         // Verify that the InCallService requires the BIND_INCALL_SERVICE permission which
2441         // enforces that only Telecom can bind to it.
2442         boolean hasServiceBindPermission = serviceInfo.permission != null &&
2443                 serviceInfo.permission.equals(
2444                         Manifest.permission.BIND_INCALL_SERVICE);
2445         if (!hasServiceBindPermission) {
2446             Log.w(this, "InCallService does not require BIND_INCALL_SERVICE permission: " +
2447                     serviceInfo.packageName);
2448             return IN_CALL_SERVICE_TYPE_INVALID;
2449         }
2450 
2451         if (mDefaultDialerCache.getSystemDialerApplication().equals(serviceInfo.packageName) &&
2452                 mDefaultDialerCache.getSystemDialerComponent().getClassName()
2453                         .equals(serviceInfo.name)) {
2454             return IN_CALL_SERVICE_TYPE_SYSTEM_UI;
2455         }
2456 
2457         // Check to see if the service holds permissions or metadata for third party apps.
2458         boolean isUIService = serviceInfo.metaData != null &&
2459                 serviceInfo.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_UI);
2460 
2461         // Check to see if the service is a car-mode UI type by checking that it has the
2462         // CONTROL_INCALL_EXPERIENCE (to verify it is a system app) and that it has the
2463         // car-mode UI metadata.
2464         // We check the permission grant on all of the packages contained in the InCallService's
2465         // same UID to see if any of them have been granted the permission.  This accomodates the
2466         // CTS tests, which have some shared UID stuff going on in order to work.  It also still
2467         // obeys the permission model since a single APK typically normally only has a single UID.
2468         String[] uidPackages = packageManager.getPackagesForUid(serviceInfo.applicationInfo.uid);
2469         boolean hasControlInCallPermission = Arrays.stream(uidPackages).anyMatch(
2470                 p -> packageManager.checkPermission(
2471                         Manifest.permission.CONTROL_INCALL_EXPERIENCE,
2472                         p) == PackageManager.PERMISSION_GRANTED);
2473 
2474         boolean hasAppOpsPermittedManageOngoingCalls = false;
2475         if (isAppOpsPermittedManageOngoingCalls(serviceInfo.applicationInfo.uid,
2476                 serviceInfo.packageName)) {
2477             hasAppOpsPermittedManageOngoingCalls = true;
2478         }
2479 
2480         boolean isCarModeUIService = serviceInfo.metaData != null &&
2481                 serviceInfo.metaData.getBoolean(
2482                         TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, false);
2483 
2484         if (isCarModeUIService && hasControlInCallPermission) {
2485             return IN_CALL_SERVICE_TYPE_CAR_MODE_UI;
2486         }
2487 
2488         // Check to see that it is the default dialer package
2489         boolean isDefaultDialerPackage = Objects.equals(serviceInfo.packageName,
2490                 mDefaultDialerCache.getDefaultDialerApplication(
2491                     userHandle.getIdentifier()));
2492         if (isDefaultDialerPackage && isUIService) {
2493             return IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI;
2494         }
2495 
2496         boolean processingBluetoothPackage = isBluetoothPackage(serviceInfo.packageName);
2497         if (mFeatureFlags.separatelyBindToBtIncallService() && processingBluetoothPackage
2498                 && (hasControlInCallPermission || hasAppOpsPermittedManageOngoingCalls)) {
2499             return IN_CALL_SERVICE_TYPE_BLUETOOTH;
2500         }
2501 
2502         // Also allow any in-call service that has the control-experience permission (to ensure
2503         // that it is a system app) and doesn't claim to show any UI.
2504         if (!isUIService && !isCarModeUIService && (hasControlInCallPermission ||
2505                 hasAppOpsPermittedManageOngoingCalls)) {
2506             return IN_CALL_SERVICE_TYPE_NON_UI;
2507         }
2508 
2509         // Anything else that remains, we will not bind to.
2510         Log.i(this, "Skipping binding to %s:%s, control: %b, car-mode: %b, ui: %b",
2511                 serviceInfo.packageName, serviceInfo.name, hasControlInCallPermission,
2512                 isCarModeUIService, isUIService);
2513         return IN_CALL_SERVICE_TYPE_INVALID;
2514     }
2515 
adjustServiceBindingsForEmergency(UserHandle userHandle)2516     private void adjustServiceBindingsForEmergency(UserHandle userHandle) {
2517         // The connected UI is not the system UI, so lets check if we should switch them
2518         // if there exists an emergency number.
2519         if (mCallsManager.isInEmergencyCall()) {
2520             mInCallServiceConnections.get(userHandle).setHasEmergency(true);
2521         }
2522     }
2523 
2524     /**
2525      * Persists the {@link IInCallService} instance and starts the communication between
2526      * this class and in-call app by sending the first update to in-call app. This method is
2527      * called after a successful binding connection is established.
2528      *
2529      * @param info Info about the service, including its {@link ComponentName}.
2530      * @param service The {@link IInCallService} implementation.
2531      * @return True if we successfully connected.
2532      */
onConnected(InCallServiceInfo info, IBinder service, UserHandle userHandle)2533     private boolean onConnected(InCallServiceInfo info, IBinder service, UserHandle userHandle) {
2534         Log.i(this, "onConnected to %s", info.getComponentName());
2535 
2536         if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
2537                 || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
2538                 || info.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI) {
2539             trackCallingUserInterfaceStarted(info);
2540         }
2541         IInCallService inCallService = IInCallService.Stub.asInterface(service);
2542         if (mFeatureFlags.separatelyBindToBtIncallService()
2543                 && info.getType() == IN_CALL_SERVICE_TYPE_BLUETOOTH) {
2544             if (!mBtBindingFuture.containsKey(userHandle)
2545                     || mBtBindingFuture.get(userHandle).isDone()) {
2546                 Log.i(this, "onConnected: BT binding future timed out.");
2547                 // Binding completed after the timeout. Clean up this binding
2548                 return false;
2549             } else {
2550                 mBtBindingFuture.get(userHandle).complete(true);
2551             }
2552             mBTInCallServices.put(userHandle, new Pair<>(info, inCallService));
2553         } else {
2554             mInCallServices.putIfAbsent(userHandle, new ArrayMap<>());
2555             mInCallServices.get(userHandle).put(info, inCallService);
2556         }
2557 
2558         if (mFeatureFlags.separatelyBindToBtIncallService()) {
2559             updateCombinedInCallServiceMap(userHandle);
2560         }
2561 
2562         try {
2563             inCallService.setInCallAdapter(
2564                     new InCallAdapter(
2565                             mCallsManager,
2566                             mCallIdMapper,
2567                             mLock,
2568                             info.getComponentName().getPackageName()));
2569         } catch (RemoteException e) {
2570             Log.e(this, e, "Failed to set the in-call adapter.");
2571             mAnomalyReporter.reportAnomaly(SET_IN_CALL_ADAPTER_ERROR_UUID,
2572                     SET_IN_CALL_ADAPTER_ERROR_MSG);
2573             Trace.endSection();
2574             return false;
2575         }
2576 
2577         // Upon successful connection, send the state of the world to the service.
2578         List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls().stream().filter(
2579                 call -> getUserFromCall(call).equals(userHandle))
2580                 .collect(Collectors.toUnmodifiableList()));
2581         Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
2582                 "calls", calls.size(), info.getComponentName());
2583         int numCallsSent = 0;
2584         for (Call call : calls) {
2585             numCallsSent += sendCallToService(call, info, inCallService);
2586         }
2587         try {
2588             inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
2589             inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
2590         } catch (RemoteException ignored) {
2591         }
2592         // Don't complete the binding future for non-ui incalls
2593         if (info.getType() != IN_CALL_SERVICE_TYPE_NON_UI && !mBindingFuture.isDone()) {
2594             mBindingFuture.complete(true);
2595         }
2596 
2597         Log.i(this, "%s calls sent to InCallService.", numCallsSent);
2598         return true;
2599     }
2600 
sendCallToService(Call call, InCallServiceInfo info, IInCallService inCallService)2601     private int sendCallToService(Call call, InCallServiceInfo info,
2602             IInCallService inCallService) {
2603         try {
2604             if ((call.isSelfManaged() && (!info.isSelfManagedCallsSupported()
2605                     || !call.visibleToInCallService())) ||
2606                     (call.isExternalCall() && !info.isExternalCallsSupported())) {
2607                 return 0;
2608             }
2609 
2610             UserHandle userFromCall = getUserFromCall(call);
2611             // Only send the RTT call if it's a UI in-call service
2612             boolean includeRttCall = false;
2613             if (mInCallServiceConnections.containsKey(userFromCall)) {
2614                 includeRttCall = info.equals(mInCallServiceConnections.get(userFromCall).getInfo());
2615             }
2616 
2617             // Track the call if we don't already know about it.
2618             addCall(call);
2619             ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
2620                     call,
2621                     true /* includeVideoProvider */,
2622                     mCallsManager.getPhoneAccountRegistrar(),
2623                     info.isExternalCallsSupported(),
2624                     includeRttCall,
2625                     info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
2626                             info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
2627             inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
2628             updateCallTracking(call, info, true /* isAdd */);
2629             return 1;
2630         } catch (RemoteException ignored) {
2631         }
2632         return 0;
2633     }
2634 
2635     /**
2636      * Cleans up an instance of in-call app after the service has been unbound.
2637      *
2638      * @param disconnectedInfo The {@link InCallServiceInfo} of the service which disconnected.
2639      */
onDisconnected(InCallServiceInfo disconnectedInfo, UserHandle userHandle)2640     private void onDisconnected(InCallServiceInfo disconnectedInfo, UserHandle userHandle) {
2641         Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
2642         if (disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
2643                 || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
2644                 || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI) {
2645             trackCallingUserInterfaceStopped(disconnectedInfo);
2646         }
2647         if (mInCallServices.containsKey(userHandle)) {
2648             mInCallServices.get(userHandle).remove(disconnectedInfo);
2649         }
2650         if (mFeatureFlags.separatelyBindToBtIncallService()
2651                 && disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_BLUETOOTH) {
2652             mBTInCallServices.remove(userHandle);
2653             updateCombinedInCallServiceMap(userHandle);
2654         }
2655     }
2656 
2657     /**
2658      * Informs all {@link InCallService} instances of the updated call information.
2659      *
2660      * @param call The {@link Call}.
2661      */
updateCall(Call call)2662     private void updateCall(Call call) {
2663         updateCall(call, false /* videoProviderChanged */, false, null);
2664     }
2665 
2666     /**
2667      * Informs all {@link InCallService} instances of the updated call information.
2668      *
2669      * @param call                 The {@link Call}.
2670      * @param videoProviderChanged {@code true} if the video provider changed, {@code false}
2671      *                             otherwise.
2672      * @param rttInfoChanged       {@code true} if any information about the RTT session changed,
2673      *                             {@code false} otherwise.
2674      * @param exceptPackageName    When specified, this package name will not get a call update.
2675      *                             Used ONLY from {@link Call#putConnectionServiceExtras(Bundle)} to
2676      *                             ensure we can propagate extras changes between InCallServices but
2677      *                             not inform the requestor of their own change.
2678      */
updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged, String exceptPackageName)2679     private void updateCall(Call call, boolean videoProviderChanged, boolean rttInfoChanged,
2680             String exceptPackageName) {
2681         UserHandle userFromCall = getUserFromCall(call);
2682         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
2683                 getCombinedInCallServiceMap();
2684         if (serviceMap.containsKey(userFromCall)) {
2685             Log.i(this, "Sending updateCall %s", call);
2686             List<ComponentName> componentsUpdated = new ArrayList<>();
2687             for (Map.Entry<InCallServiceInfo, IInCallService> entry : serviceMap.
2688                     get(userFromCall).entrySet()) {
2689                 InCallServiceInfo info = entry.getKey();
2690                 ComponentName componentName = info.getComponentName();
2691 
2692                 // If specified, skip ICS if it matches the package name.  Used for cases where on
2693                 // ICS makes an update to extras and we want to skip updating the same ICS with the
2694                 // change that it implemented.
2695                 if (exceptPackageName != null
2696                         && componentName.getPackageName().equals(exceptPackageName)) {
2697                     continue;
2698                 }
2699 
2700                 if (call.isExternalCall() && !info.isExternalCallsSupported()) {
2701                     continue;
2702                 }
2703 
2704                 if (call.isSelfManaged() && (!call.visibleToInCallService()
2705                         || !info.isSelfManagedCallsSupported())) {
2706                     continue;
2707                 }
2708 
2709                 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
2710                         call,
2711                         videoProviderChanged /* includeVideoProvider */,
2712                         mCallsManager.getPhoneAccountRegistrar(),
2713                         info.isExternalCallsSupported(),
2714                         rttInfoChanged && info.equals(
2715                                 mInCallServiceConnections.get(userFromCall).getInfo()),
2716                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
2717                         info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
2718                 IInCallService inCallService = entry.getValue();
2719                 componentsUpdated.add(componentName);
2720 
2721                 if (info.getType() == IN_CALL_SERVICE_TYPE_BLUETOOTH
2722                         && call.getState() == CallState.DISCONNECTED
2723                         && !mDisconnectedToneBtFutures.containsKey(call.getId())) {
2724                     CompletableFuture<Void> disconnectedToneFuture = new CompletableFuture<Void>()
2725                             .completeOnTimeout(null, DISCONNECTED_TONE_TIMEOUT,
2726                                     TimeUnit.MILLISECONDS);
2727                     mDisconnectedToneBtFutures.put(call.getId(), disconnectedToneFuture);
2728                     mDisconnectedToneBtFutures.get(call.getId()).thenRunAsync(() -> {
2729                         Log.i(this, "updateCall: Sending call disconnected update to BT ICS.");
2730                         updateCallToIcs(inCallService, info, parcelableCall, componentName);
2731                         mDisconnectedToneBtFutures.remove(call.getId());
2732                     }, new LoggedHandlerExecutor(mHandler, "ICC.uC", mLock));
2733                 } else {
2734                     updateCallToIcs(inCallService, info, parcelableCall, componentName);
2735                 }
2736             }
2737             Log.i(this, "Components updated: %s", componentsUpdated);
2738         } else {
2739             Log.i(this,
2740                     "Unable to update call. InCallService not found for user: %s", userFromCall);
2741         }
2742     }
2743 
updateCallToIcs(IInCallService inCallService, InCallServiceInfo info, ParcelableCall parcelableCall, ComponentName componentName)2744     private void updateCallToIcs(IInCallService inCallService, InCallServiceInfo info,
2745             ParcelableCall parcelableCall, ComponentName componentName) {
2746         try {
2747             inCallService.updateCall(
2748                     sanitizeParcelableCallForService(info, parcelableCall));
2749         } catch (RemoteException exception) {
2750             Log.w(this, "Call status update did not send to: "
2751                     + componentName + " successfully with error " + exception);
2752         }
2753     }
2754 
2755     /**
2756      * Adds the call to the list of calls tracked by the {@link InCallController}.
2757      * @param call The call to add.
2758      */
2759     @VisibleForTesting
addCall(Call call)2760     public void addCall(Call call) {
2761         if (call == null) {
2762             return;
2763         }
2764 
2765         if (mCallIdMapper.getCalls().size() == 0) {
2766             mAppOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
2767                     java.lang.Runnable::run, this);
2768             updateAllCarrierPrivileged();
2769             updateAllCarrierPrivilegedUsingMic();
2770         }
2771 
2772         if (mCallIdMapper.getCallId(call) == null) {
2773             mCallIdMapper.addCall(call);
2774             call.addListener(mCallListener);
2775             if (mFeatureFlags.separatelyBindToBtIncallService()) {
2776                 mPendingEndToneCall.add(call);
2777             }
2778         }
2779 
2780         maybeTrackMicrophoneUse(isMuted());
2781     }
2782 
2783     /**
2784      * @return true if we are bound to the UI InCallService and it is connected.
2785      */
isBoundAndConnectedToServices(UserHandle userHandle)2786     private boolean isBoundAndConnectedToServices(UserHandle userHandle) {
2787         if (!mInCallServiceConnections.containsKey(userHandle)) {
2788             return false;
2789         }
2790         return mInCallServiceConnections.get(userHandle).isConnected();
2791     }
2792 
2793     @VisibleForTesting
isBoundAndConnectedToBTService(UserHandle userHandle)2794     public boolean isBoundAndConnectedToBTService(UserHandle userHandle) {
2795         if (!mBTInCallServiceConnections.containsKey(userHandle)) {
2796             return false;
2797         }
2798         return mBTInCallServiceConnections.get(userHandle).isConnected();
2799     }
2800 
2801     /**
2802      * @return A future that is pending whenever we are in the middle of binding to an
2803      *         incall service.
2804      */
getBindingFuture()2805     public CompletableFuture<Boolean> getBindingFuture() {
2806         return mBindingFuture;
2807     }
2808 
2809     /**
2810      * @return A future that is pending whenever we are in the middle of binding to the BT
2811      *         incall service.
2812      */
getBtBindingFuture(Call call)2813     public CompletableFuture<Boolean> getBtBindingFuture(Call call) {
2814         UserHandle userHandle = getUserFromCall(call);
2815         return mBtBindingFuture.get(userHandle);
2816     }
2817 
2818     /**
2819      * @return A future that is pending whenever we are in the process of sending the call
2820      *         disconnected state to the BT ICS so that the disconnect tone can finish playing.
2821      */
getDisconnectedToneBtFutures()2822     public Map<String, CompletableFuture<Void>> getDisconnectedToneBtFutures() {
2823         return mDisconnectedToneBtFutures;
2824     }
2825 
2826     /**
2827      * Dumps the state of the {@link InCallController}.
2828      *
2829      * @param pw The {@code IndentingPrintWriter} to write the state to.
2830      */
dump(IndentingPrintWriter pw)2831     public void dump(IndentingPrintWriter pw) {
2832         pw.println("combinedInCallServiceMap (InCalls registered):");
2833         pw.increaseIndent();
2834         Map<UserHandle, Map<InCallController.InCallServiceInfo, IInCallService>> serviceMap =
2835                 getCombinedInCallServiceMap();
2836         serviceMap.values().forEach(inCallServices -> {
2837             for (InCallServiceInfo info : inCallServices.keySet()) {
2838                 pw.println(info);
2839             }
2840         });
2841         pw.decreaseIndent();
2842 
2843         pw.println("ServiceConnections (InCalls bound):");
2844         pw.increaseIndent();
2845         for (InCallServiceConnection inCallServiceConnection : mInCallServiceConnections.values()) {
2846             inCallServiceConnection.dump(pw);
2847         }
2848         pw.decreaseIndent();
2849 
2850         mCarModeTracker.dump(pw);
2851     }
2852 
2853     /**
2854      * @return The package name of the UI which is currently bound, or null if none.
2855      */
getConnectedUi(UserHandle userHandle)2856     private ComponentName getConnectedUi(UserHandle userHandle) {
2857         if (mInCallServices.containsKey(userHandle)) {
2858             InCallServiceInfo connectedUi = mInCallServices.get(
2859                             userHandle).keySet().stream().filter(
2860                             i -> i.getType() == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI
2861                                     || i.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI)
2862                     .findAny()
2863                     .orElse(null);
2864             if (connectedUi != null) {
2865                 return connectedUi.mComponentName;
2866             }
2867         }
2868         return null;
2869     }
2870 
doesConnectedDialerSupportRinging(UserHandle userHandle)2871     public boolean doesConnectedDialerSupportRinging(UserHandle userHandle) {
2872         String ringingPackage =  null;
2873 
2874         ComponentName connectedPackage = getConnectedUi(userHandle);
2875         if (connectedPackage != null) {
2876             ringingPackage = connectedPackage.getPackageName().trim();
2877             Log.d(this, "doesConnectedDialerSupportRinging: alreadyConnectedPackage=%s",
2878                     ringingPackage);
2879         }
2880 
2881         if (TextUtils.isEmpty(ringingPackage)) {
2882             // The current in-call UI returned nothing, so lets use the default dialer.
2883             ringingPackage = mDefaultDialerCache.getRoleManagerAdapter().getDefaultDialerApp(
2884                     userHandle.getIdentifier());
2885             if (ringingPackage != null) {
2886                 Log.d(this, "doesConnectedDialerSupportRinging: notCurentlyConnectedPackage=%s",
2887                         ringingPackage);
2888             }
2889         }
2890         if (TextUtils.isEmpty(ringingPackage)) {
2891             Log.w(this, "doesConnectedDialerSupportRinging: no default dialer found; oh no!");
2892             return false;
2893         }
2894 
2895         Intent intent = new Intent(InCallService.SERVICE_INTERFACE)
2896             .setPackage(ringingPackage);
2897         List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
2898                 intent, PackageManager.GET_META_DATA,
2899                 userHandle.getIdentifier());
2900         if (entries.isEmpty()) {
2901             Log.w(this, "doesConnectedDialerSupportRinging: couldn't find dialer's package info"
2902                     + " <sad trombone>");
2903             return false;
2904         }
2905 
2906         ResolveInfo info = entries.get(0);
2907         if (info.serviceInfo == null || info.serviceInfo.metaData == null) {
2908             Log.w(this, "doesConnectedDialerSupportRinging: couldn't find dialer's metadata"
2909                     + " <even sadder trombone>");
2910             return false;
2911         }
2912 
2913         return info.serviceInfo.metaData
2914                 .getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_RINGING, false);
2915     }
2916 
orderCallsWithChildrenFirst(Collection<Call> calls)2917     private List<Call> orderCallsWithChildrenFirst(Collection<Call> calls) {
2918         LinkedList<Call> parentCalls = new LinkedList<>();
2919         LinkedList<Call> childCalls = new LinkedList<>();
2920         for (Call call : calls) {
2921             if (call.getChildCalls().size() > 0) {
2922                 parentCalls.add(call);
2923             } else {
2924                 childCalls.add(call);
2925             }
2926         }
2927         childCalls.addAll(parentCalls);
2928         return childCalls;
2929     }
2930 
2931     @VisibleForTesting
sanitizeParcelableCallForService( InCallServiceInfo info, ParcelableCall parcelableCall)2932     public ParcelableCall sanitizeParcelableCallForService(
2933             InCallServiceInfo info, ParcelableCall parcelableCall) {
2934         ParcelableCall.ParcelableCallBuilder builder =
2935                 ParcelableCall.ParcelableCallBuilder.fromParcelableCall(parcelableCall);
2936         PackageManager pm = mContext.getPackageManager();
2937 
2938         // Check for contacts permission.
2939         if (pm.checkPermission(Manifest.permission.READ_CONTACTS,
2940                 info.getComponentName().getPackageName()) != PackageManager.PERMISSION_GRANTED) {
2941             // contacts permission is not present...
2942 
2943             // removing the contactsDisplayName
2944             builder.setContactDisplayName(null);
2945             builder.setContactPhotoUri(null);
2946 
2947             // removing the Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB extra
2948             if (parcelableCall.getExtras() != null) {
2949                 Bundle callBundle = parcelableCall.getExtras();
2950                 if (callBundle.containsKey(
2951                         android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB)) {
2952                     Bundle newBundle = callBundle.deepCopy();
2953                     newBundle.remove(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB);
2954                     builder.setExtras(newBundle);
2955                 }
2956             }
2957         }
2958 
2959         // TODO: move all the other service-specific sanitizations in here
2960         return builder.createParcelableCall();
2961     }
2962 
2963     @VisibleForTesting
getHandler()2964     public Handler getHandler() {
2965         return mHandler;
2966     }
2967 
2968     /**
2969      * Determines if the specified package is a valid car mode {@link InCallService}.
2970      * @param packageName The package name to check.
2971      * @return {@code true} if the package has a valid car mode {@link InCallService} defined,
2972      * {@code false} otherwise.
2973      */
isCarModeInCallService(@onNull String packageName)2974     private boolean isCarModeInCallService(@NonNull String packageName) {
2975         // Disabled InCallService should also be considered as a valid InCallService here so that
2976         // it can be added to the CarModeTracker, in case it will be enabled in future.
2977         InCallServiceInfo info =
2978                 getInCallServiceComponent(mCallsManager.getCurrentUserHandle(),
2979                         packageName, IN_CALL_SERVICE_TYPE_CAR_MODE_UI, false /* ignoreDisabled */);
2980         return info != null && info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI;
2981     }
2982 
handleCarModeChange(int priority, String packageName, boolean isCarMode)2983     public void handleCarModeChange(int priority, String packageName, boolean isCarMode) {
2984         Log.i(this, "handleCarModeChange: packageName=%s, priority=%d, isCarMode=%b",
2985                 packageName, priority, isCarMode);
2986         if (packageName == null) {
2987             Log.i(this, "handleCarModeChange: Got null packageName, ignoring");
2988             return;
2989         }
2990         // Don't ignore the signal if we are disabling car mode; package may be uninstalled.
2991         if (isCarMode && !isCarModeInCallService(packageName)) {
2992             Log.i(this, "handleCarModeChange: not a valid InCallService; packageName=%s",
2993                     packageName);
2994             return;
2995         }
2996 
2997         if (isCarMode) {
2998             mCarModeTracker.handleEnterCarMode(priority, packageName);
2999         } else {
3000             mCarModeTracker.handleExitCarMode(priority, packageName);
3001         }
3002 
3003         updateCarModeForConnections();
3004     }
3005 
handleSetAutomotiveProjection(@onNull String packageName)3006     public void handleSetAutomotiveProjection(@NonNull String packageName) {
3007         Log.i(this, "handleSetAutomotiveProjection: packageName=%s", packageName);
3008         if (!isCarModeInCallService(packageName)) {
3009             Log.i(this, "handleSetAutomotiveProjection: not a valid InCallService: packageName=%s",
3010                     packageName);
3011             return;
3012         }
3013         mCarModeTracker.handleSetAutomotiveProjection(packageName);
3014 
3015         updateCarModeForConnections();
3016     }
3017 
handleReleaseAutomotiveProjection()3018     public void handleReleaseAutomotiveProjection() {
3019         Log.i(this, "handleReleaseAutomotiveProjection");
3020         mCarModeTracker.handleReleaseAutomotiveProjection();
3021 
3022         updateCarModeForConnections();
3023     }
3024 
updateCarModeForConnections()3025     public void updateCarModeForConnections() {
3026         Log.i(this, "updateCarModeForConnections: car mode apps: %s",
3027                 mCarModeTracker.getCarModeApps().stream().collect(Collectors.joining(", ")));
3028 
3029         UserManager um = mContext.getSystemService(UserManager.class);
3030         UserHandle currentUser = mCallsManager.getCurrentUserHandle();
3031         UserHandle childUser = findChildManagedProfileUser(currentUser, um);
3032 
3033         CarSwappingInCallServiceConnection inCallServiceConnectionForCurrentUser = null;
3034         CarSwappingInCallServiceConnection inCallServiceConnectionForChildUser = null;
3035 
3036         Log.i(this, "update carmode current:%s parent:%s", currentUser, childUser);
3037         if (mInCallServiceConnections.containsKey(currentUser)) {
3038             inCallServiceConnectionForCurrentUser = mInCallServiceConnections.
3039                     get(currentUser);
3040         }
3041         if (childUser != null && mInCallServiceConnections.containsKey(childUser)) {
3042             inCallServiceConnectionForChildUser = mInCallServiceConnections.
3043                     get(childUser);
3044         }
3045 
3046         if (shouldUseCarModeUI()) {
3047             Log.i(this, "updateCarModeForConnections: potentially update car mode app.");
3048             //always pass current user to changeCarMode. That will ultimately be used for bindAsUser
3049             if (inCallServiceConnectionForCurrentUser != null) {
3050                 inCallServiceConnectionForCurrentUser.changeCarModeApp(
3051                         mCarModeTracker.getCurrentCarModePackage(),
3052                         currentUser);
3053             }
3054             if (inCallServiceConnectionForChildUser != null) {
3055                 inCallServiceConnectionForChildUser.changeCarModeApp(
3056                         mCarModeTracker.getCurrentCarModePackage(),
3057                         currentUser);
3058             }
3059         } else {
3060             if (inCallServiceConnectionForCurrentUser != null
3061                     && inCallServiceConnectionForCurrentUser.isCarMode()) {
3062                 Log.i(this, "updateCarModeForConnections: car mode no longer "
3063                         + "applicable for current user; disabling");
3064                 inCallServiceConnectionForCurrentUser.disableCarMode();
3065             }
3066             if (inCallServiceConnectionForChildUser != null
3067                     && inCallServiceConnectionForChildUser.isCarMode()) {
3068                 Log.i(this, "updateCarModeForConnections: car mode no longer "
3069                         + "applicable for child user; disabling");
3070                 inCallServiceConnectionForChildUser.disableCarMode();
3071             }
3072         }
3073     }
3074 
3075     /**
3076      * Tracks start of microphone use on binding to the current calling UX.
3077      * @param info
3078      */
trackCallingUserInterfaceStarted(InCallServiceInfo info)3079     private void trackCallingUserInterfaceStarted(InCallServiceInfo info) {
3080         String packageName = info.getComponentName().getPackageName();
3081         if (!Objects.equals(mCurrentUserInterfacePackageName, packageName)) {
3082             Log.i(this, "trackCallingUserInterfaceStarted: %s is now calling UX.", packageName);
3083             mCurrentUserInterfacePackageName = packageName;
3084         }
3085         maybeTrackMicrophoneUse(isMuted());
3086     }
3087 
3088     /**
3089      * Tracks stop of microphone use on unbind from the current calling UX.
3090      * @param info
3091      */
trackCallingUserInterfaceStopped(InCallServiceInfo info)3092     private void trackCallingUserInterfaceStopped(InCallServiceInfo info) {
3093         maybeTrackMicrophoneUse(isMuted());
3094         mCurrentUserInterfacePackageName = null;
3095         String packageName = info.getComponentName().getPackageName();
3096         Log.i(this, "trackCallingUserInterfaceStopped: %s is no longer calling UX", packageName);
3097     }
3098 
maybeTrackMicrophoneUse(boolean isMuted)3099     private void maybeTrackMicrophoneUse(boolean isMuted) {
3100         maybeTrackMicrophoneUse(isMuted, false);
3101     }
3102 
3103     /**
3104      * As calls are added, removed and change between external and non-external status, track
3105      * whether the current active calling UX is using the microphone.  We assume if there is a
3106      * managed call present and the mic is not muted that the microphone is in use.
3107      */
maybeTrackMicrophoneUse(boolean isMuted, boolean isScheduledDelay)3108     private void maybeTrackMicrophoneUse(boolean isMuted, boolean isScheduledDelay) {
3109         if (mIsStartCallDelayScheduled && !isScheduledDelay) {
3110             return;
3111         }
3112 
3113         mIsStartCallDelayScheduled = false;
3114         boolean wasUsingMicrophone = mIsCallUsingMicrophone;
3115         boolean wasTrackingCall = mIsTrackingManagedAliveCall;
3116         mIsTrackingManagedAliveCall = isTrackingManagedAliveCall();
3117         if (!wasTrackingCall && mIsTrackingManagedAliveCall) {
3118             mIsStartCallDelayScheduled = true;
3119             mHandler.postDelayed(new Runnable("ICC.mTMU", mLock) {
3120                 @Override
3121                 public void loggedRun() {
3122                     maybeTrackMicrophoneUse(isMuted(), true);
3123                 }
3124             }.prepare(), mTimeoutsAdapter.getCallStartAppOpDebounceIntervalMillis());
3125             return;
3126         }
3127 
3128         mIsCallUsingMicrophone = mIsTrackingManagedAliveCall && !isMuted
3129                 && !isCarrierPrivilegedUsingMicDuringVoipCall();
3130         if (wasUsingMicrophone != mIsCallUsingMicrophone) {
3131             int opPackageUid = getOpPackageUid();
3132             if (mIsCallUsingMicrophone) {
3133                 // Note, not checking return value, as this op call is merely for tracing use
3134                 mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, opPackageUid,
3135                         mContext.getOpPackageName(), false, null, null);
3136                 mSensorPrivacyManager.showSensorUseDialog(SensorPrivacyManager.Sensors.MICROPHONE);
3137             } else {
3138                 mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, opPackageUid,
3139                         mContext.getOpPackageName(), null);
3140             }
3141         }
3142     }
3143 
3144     /**
3145      * Returns the uid of the package in the current user to be used for app ops attribution.
3146      */
getOpPackageUid()3147     private int getOpPackageUid() {
3148         UserHandle user = mCallsManager.getCurrentUserHandle();
3149 
3150         try {
3151             PackageManager pkgManager = mContext.getPackageManager();
3152             return pkgManager.getPackageUidAsUser(mContext.getOpPackageName(),
3153                     user.getIdentifier());
3154         } catch (PackageManager.NameNotFoundException e) {
3155             Log.e(this, e, "getPackageForAssociatedUser: could not find package %s"
3156                     + " for user %s", mContext.getOpPackageName(), user);
3157             // fallback to current process id - this should not happen
3158             return myUid();
3159         }
3160     }
3161 
3162     /**
3163      * @return {@code true} if InCallController is tracking a managed call (i.e. not self managed
3164      * and not external) that is active.
3165      */
isTrackingManagedAliveCall()3166     private boolean isTrackingManagedAliveCall() {
3167         return mCallIdMapper.getCalls().stream().anyMatch(c -> !c.isExternalCall()
3168             && !c.isSelfManaged() && c.isAlive() && ArrayUtils.contains(LIVE_CALL_STATES,
3169                 c.getState()));
3170     }
3171 
isCarrierPrivilegedUsingMicDuringVoipCall()3172     private boolean isCarrierPrivilegedUsingMicDuringVoipCall() {
3173         return !mActiveCarrierPrivilegedApps.isEmpty() &&
3174                 mCallIdMapper.getCalls().stream().anyMatch(Call::getIsVoipAudioMode);
3175     }
3176 
3177     /**
3178      * @return {@code true} if the audio is currently muted, {@code false} otherwise.
3179      */
isMuted()3180     private boolean isMuted() {
3181         if (mCallsManager.getAudioState() == null) {
3182             return false;
3183         }
3184         return mCallsManager.getAudioState().isMuted();
3185     }
3186 
isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage)3187     private boolean isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage) {
3188         return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(mContext,
3189                 Manifest.permission.MANAGE_ONGOING_CALLS, PermissionChecker.PID_UNKNOWN,
3190                         new AttributionSource(mContext.getAttributionSource(),
3191                                 new AttributionSource(uid, callingPackage,
3192                                         /*attributionTag*/ null)), "Checking whether the app has"
3193                                                 + " MANAGE_ONGOING_CALLS permission")
3194                                                         == PermissionChecker.PERMISSION_GRANTED;
3195     }
3196 
sendCrashedInCallServiceNotification(String packageName, UserHandle userHandle)3197     private void sendCrashedInCallServiceNotification(String packageName, UserHandle userHandle) {
3198         PackageManager packageManager = mContext.getPackageManager();
3199         CharSequence appName;
3200         String systemDialer = mDefaultDialerCache.getSystemDialerApplication();
3201         if ((systemDialer != null) && systemDialer.equals(packageName)) {
3202             return;
3203         }
3204         try {
3205             appName = packageManager.getApplicationLabel(
3206                     packageManager.getApplicationInfo(packageName, 0));
3207             if (TextUtils.isEmpty(appName)) {
3208                 appName = packageName;
3209             }
3210         } catch (PackageManager.NameNotFoundException e) {
3211             appName = packageName;
3212         }
3213         NotificationManager notificationManager = (NotificationManager) mContext
3214                 .getSystemService(Context.NOTIFICATION_SERVICE);
3215         Notification.Builder builder = new Notification.Builder(mContext,
3216                 NotificationChannelManager.CHANNEL_ID_IN_CALL_SERVICE_CRASH);
3217         builder.setSmallIcon(R.drawable.ic_phone)
3218                 .setColor(mContext.getResources().getColor(R.color.theme_color))
3219                 .setContentTitle(
3220                         mContext.getString(
3221                                 R.string.notification_incallservice_not_responding_title, appName))
3222                 .setStyle(new Notification.BigTextStyle()
3223                         .bigText(mContext.getText(
3224                                 R.string.notification_incallservice_not_responding_body)));
3225         notificationManager.notifyAsUser(NOTIFICATION_TAG, IN_CALL_SERVICE_NOTIFICATION_ID,
3226                 builder.build(), userHandle);
3227     }
3228 
updateCallTracking(Call call, InCallServiceInfo info, boolean isAdd)3229     private void updateCallTracking(Call call, InCallServiceInfo info, boolean isAdd) {
3230         int type = info.getType();
3231         boolean hasUi = type == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
3232                 || type == IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI;
3233         call.maybeOnInCallServiceTrackingChanged(isAdd, hasUi);
3234     }
3235 
getUserFromCall(Call call)3236     private UserHandle getUserFromCall(Call call) {
3237         // Call may never be specified, so we can fall back to using the CallManager current user.
3238         if (call == null) {
3239             return mCallsManager.getCurrentUserHandle();
3240         } else {
3241             UserHandle userFromCall = call.getAssociatedUser();
3242             UserManager userManager = mFeatureFlags.telecomResolveHiddenDependencies()
3243                     ? mContext.createContextAsUser(mCallsManager.getCurrentUserHandle(), 0)
3244                             .getSystemService(UserManager.class)
3245                     : mContext.getSystemService(UserManager.class);
3246             boolean isCurrentUserAdmin = mFeatureFlags.telecomResolveHiddenDependencies()
3247                     ? userManager.isAdminUser()
3248                     : userManager.isUserAdmin(mCallsManager.getCurrentUserHandle().getIdentifier());
3249             // Emergency call should never be blocked, so if the user associated with the target
3250             // phone account handle user is in quiet mode, use the current user for the ecall.
3251             // Note, that this only applies to incoming calls that are received on assigned
3252             // sims (i.e. work sim), where the associated user would be the target phone account
3253             // handle user.
3254             if ((call.isEmergencyCall() || call.isInECBM())
3255                     && (userManager.isQuietModeEnabled(userFromCall)
3256                     // We should also account for secondary/guest users where the profile may not
3257                     // necessarily be paused.
3258                     || !isCurrentUserAdmin)) {
3259                 return mCallsManager.getCurrentUserHandle();
3260             }
3261             return userFromCall;
3262         }
3263     }
3264 
3265     /**
3266      * Useful for debugging purposes and called on the command line via
3267      * an "adb shell telecom command".
3268      *
3269      * @return true if a particular non-ui InCallService package is bound in a call.
3270      */
isNonUiInCallServiceBound(String packageName)3271     public boolean isNonUiInCallServiceBound(String packageName) {
3272         for (NonUIInCallServiceConnectionCollection ics : mNonUIInCallServiceConnections.values()) {
3273             for (InCallServiceBindingConnection connection : ics.getSubConnections()) {
3274                 InCallServiceInfo serviceInfo = connection.mInCallServiceInfo;
3275                 Log.i(this, "isNonUiInCallServiceBound: found serviceInfo=[%s]", serviceInfo);
3276                 if (serviceInfo != null &&
3277                         serviceInfo.mComponentName.getPackageName().contains(packageName)) {
3278                     Log.i(this, "isNonUiInCallServiceBound: found target package");
3279                     return true;
3280                 }
3281             }
3282         }
3283         // If early binding for BT ICS is enabled, ensure that it is included into consideration as
3284         // a bound non-UI ICS.
3285         return mFeatureFlags.separatelyBindToBtIncallService() && !mBTInCallServices.isEmpty()
3286                 && isBluetoothPackage(packageName);
3287     }
3288 
updateCombinedInCallServiceMap(UserHandle user)3289     private void updateCombinedInCallServiceMap(UserHandle user) {
3290         synchronized (mLock) {
3291             Map<InCallServiceInfo, IInCallService> serviceMap;
3292             if (mInCallServices.containsKey(user)) {
3293                 serviceMap = mInCallServices.get(user);
3294             } else {
3295                 serviceMap = new HashMap<>();
3296             }
3297             if (mFeatureFlags.separatelyBindToBtIncallService()
3298                     && mBTInCallServices.containsKey(user)) {
3299                 Pair<InCallServiceInfo, IInCallService> btServicePair = mBTInCallServices.get(user);
3300                 serviceMap.put(btServicePair.first, btServicePair.second);
3301             }
3302             if (!serviceMap.isEmpty()) {
3303                 mCombinedInCallServiceMap.put(user, serviceMap);
3304             } else {
3305                 mCombinedInCallServiceMap.remove(user);
3306             }
3307         }
3308     }
3309 
3310     private Map<UserHandle,
getCombinedInCallServiceMap()3311             Map<InCallController.InCallServiceInfo, IInCallService>> getCombinedInCallServiceMap() {
3312         synchronized (mLock) {
3313             if (mFeatureFlags.separatelyBindToBtIncallService()) {
3314                 return mCombinedInCallServiceMap;
3315             } else {
3316                 return mInCallServices;
3317             }
3318         }
3319     }
3320 
isBluetoothPackage(String packageName)3321     private boolean isBluetoothPackage(String packageName) {
3322         for (String pkgName : mDefaultDialerCache.getBTInCallServicePackages()) {
3323             if (pkgName.equals(packageName)) {
3324                 return true;
3325             }
3326         }
3327         return false;
3328     }
3329 }
3330