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