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