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