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