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