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