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