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