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