1 /* 2 * Copyright (C) 2013 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.incallui; 18 19 import com.android.contacts.common.CallUtil; 20 import com.android.contacts.common.testing.NeededForTesting; 21 22 import android.content.Context; 23 import android.hardware.camera2.CameraCharacteristics; 24 import android.net.Uri; 25 import android.os.Bundle; 26 import android.os.Trace; 27 import android.telecom.Connection; 28 import android.telecom.DisconnectCause; 29 import android.telecom.GatewayInfo; 30 import android.telecom.InCallService.VideoCall; 31 import android.telecom.PhoneAccount; 32 import android.telecom.PhoneAccountHandle; 33 import android.telecom.TelecomManager; 34 import android.telecom.VideoProfile; 35 import android.telephony.PhoneNumberUtils; 36 import android.text.TextUtils; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Locale; 41 import java.util.Objects; 42 43 /** 44 * Describes a single call and its state. 45 */ 46 @NeededForTesting 47 public class Call { 48 /* Defines different states of this call */ 49 public static class State { 50 public static final int INVALID = 0; 51 public static final int NEW = 1; /* The call is new. */ 52 public static final int IDLE = 2; /* The call is idle. Nothing active */ 53 public static final int ACTIVE = 3; /* There is an active call */ 54 public static final int INCOMING = 4; /* A normal incoming phone call */ 55 public static final int CALL_WAITING = 5; /* Incoming call while another is active */ 56 public static final int DIALING = 6; /* An outgoing call during dial phase */ 57 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */ 58 public static final int ONHOLD = 8; /* An active phone call placed on hold */ 59 public static final int DISCONNECTING = 9; /* A call is being ended. */ 60 public static final int DISCONNECTED = 10; /* State after a call disconnects */ 61 public static final int CONFERENCED = 11; /* Call part of a conference call */ 62 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */ 63 public static final int CONNECTING = 13; /* Waiting for Telecomm broadcast to finish */ 64 65 isConnectingOrConnected(int state)66 public static boolean isConnectingOrConnected(int state) { 67 switch(state) { 68 case ACTIVE: 69 case INCOMING: 70 case CALL_WAITING: 71 case CONNECTING: 72 case DIALING: 73 case REDIALING: 74 case ONHOLD: 75 case CONFERENCED: 76 return true; 77 default: 78 } 79 return false; 80 } 81 isDialing(int state)82 public static boolean isDialing(int state) { 83 return state == DIALING || state == REDIALING; 84 } 85 toString(int state)86 public static String toString(int state) { 87 switch (state) { 88 case INVALID: 89 return "INVALID"; 90 case NEW: 91 return "NEW"; 92 case IDLE: 93 return "IDLE"; 94 case ACTIVE: 95 return "ACTIVE"; 96 case INCOMING: 97 return "INCOMING"; 98 case CALL_WAITING: 99 return "CALL_WAITING"; 100 case DIALING: 101 return "DIALING"; 102 case REDIALING: 103 return "REDIALING"; 104 case ONHOLD: 105 return "ONHOLD"; 106 case DISCONNECTING: 107 return "DISCONNECTING"; 108 case DISCONNECTED: 109 return "DISCONNECTED"; 110 case CONFERENCED: 111 return "CONFERENCED"; 112 case SELECT_PHONE_ACCOUNT: 113 return "SELECT_PHONE_ACCOUNT"; 114 case CONNECTING: 115 return "CONNECTING"; 116 default: 117 return "UNKNOWN"; 118 } 119 } 120 } 121 122 /** 123 * Defines different states of session modify requests, which are used to upgrade to video, or 124 * downgrade to audio. 125 */ 126 public static class SessionModificationState { 127 public static final int NO_REQUEST = 0; 128 public static final int WAITING_FOR_RESPONSE = 1; 129 public static final int REQUEST_FAILED = 2; 130 public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3; 131 public static final int UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4; 132 public static final int REQUEST_REJECTED = 5; 133 } 134 135 public static class VideoSettings { 136 public static final int CAMERA_DIRECTION_UNKNOWN = -1; 137 public static final int CAMERA_DIRECTION_FRONT_FACING = 138 CameraCharacteristics.LENS_FACING_FRONT; 139 public static final int CAMERA_DIRECTION_BACK_FACING = 140 CameraCharacteristics.LENS_FACING_BACK; 141 142 private int mCameraDirection = CAMERA_DIRECTION_UNKNOWN; 143 144 /** 145 * Sets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN, 146 * the video state of the call should be used to infer the camera direction. 147 * 148 * @see {@link CameraCharacteristics#LENS_FACING_FRONT} 149 * @see {@link CameraCharacteristics#LENS_FACING_BACK} 150 */ setCameraDir(int cameraDirection)151 public void setCameraDir(int cameraDirection) { 152 if (cameraDirection == CAMERA_DIRECTION_FRONT_FACING 153 || cameraDirection == CAMERA_DIRECTION_BACK_FACING) { 154 mCameraDirection = cameraDirection; 155 } else { 156 mCameraDirection = CAMERA_DIRECTION_UNKNOWN; 157 } 158 } 159 160 /** 161 * Gets the camera direction. if camera direction is set to CAMERA_DIRECTION_UNKNOWN, 162 * the video state of the call should be used to infer the camera direction. 163 * 164 * @see {@link CameraCharacteristics#LENS_FACING_FRONT} 165 * @see {@link CameraCharacteristics#LENS_FACING_BACK} 166 */ getCameraDir()167 public int getCameraDir() { 168 return mCameraDirection; 169 } 170 toString()171 public String toString() { 172 return "(CameraDir:" + getCameraDir() + ")"; 173 } 174 } 175 176 177 private static final String ID_PREFIX = Call.class.getSimpleName() + "_"; 178 private static int sIdCounter = 0; 179 180 private android.telecom.Call.Callback mTelecomCallCallback = 181 new android.telecom.Call.Callback() { 182 @Override 183 public void onStateChanged(android.telecom.Call call, int newState) { 184 Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " newState=" 185 + newState); 186 update(); 187 } 188 189 @Override 190 public void onParentChanged(android.telecom.Call call, 191 android.telecom.Call newParent) { 192 Log.d(this, "TelecommCallCallback onParentChanged call=" + call + " newParent=" 193 + newParent); 194 update(); 195 } 196 197 @Override 198 public void onChildrenChanged(android.telecom.Call call, 199 List<android.telecom.Call> children) { 200 update(); 201 } 202 203 @Override 204 public void onDetailsChanged(android.telecom.Call call, 205 android.telecom.Call.Details details) { 206 Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " details=" 207 + details); 208 update(); 209 } 210 211 @Override 212 public void onCannedTextResponsesLoaded(android.telecom.Call call, 213 List<String> cannedTextResponses) { 214 Log.d(this, "TelecommCallCallback onStateChanged call=" + call 215 + " cannedTextResponses=" + cannedTextResponses); 216 update(); 217 } 218 219 @Override 220 public void onPostDialWait(android.telecom.Call call, 221 String remainingPostDialSequence) { 222 Log.d(this, "TelecommCallCallback onStateChanged call=" + call 223 + " remainingPostDialSequence=" + remainingPostDialSequence); 224 update(); 225 } 226 227 @Override 228 public void onVideoCallChanged(android.telecom.Call call, 229 VideoCall videoCall) { 230 Log.d(this, "TelecommCallCallback onStateChanged call=" + call + " videoCall=" 231 + videoCall); 232 update(); 233 } 234 235 @Override 236 public void onCallDestroyed(android.telecom.Call call) { 237 Log.d(this, "TelecommCallCallback onStateChanged call=" + call); 238 call.unregisterCallback(mTelecomCallCallback); 239 } 240 241 @Override 242 public void onConferenceableCallsChanged(android.telecom.Call call, 243 List<android.telecom.Call> conferenceableCalls) { 244 update(); 245 } 246 }; 247 248 private android.telecom.Call mTelecommCall; 249 private boolean mIsEmergencyCall; 250 private Uri mHandle; 251 private final String mId; 252 private int mState = State.INVALID; 253 private DisconnectCause mDisconnectCause; 254 private int mSessionModificationState; 255 private final List<String> mChildCallIds = new ArrayList<>(); 256 private final VideoSettings mVideoSettings = new VideoSettings(); 257 /** 258 * mModifyToVideoState is used to store requested upgrade / downgrade video state 259 */ 260 private int mModifyToVideoState = VideoProfile.STATE_AUDIO_ONLY; 261 262 private InCallVideoCallCallback mVideoCallCallback; 263 private String mChildNumber; 264 private String mLastForwardedNumber; 265 private String mCallSubject; 266 private PhoneAccountHandle mPhoneAccountHandle; 267 268 /** 269 * Indicates whether the phone account associated with this call supports specifying a call 270 * subject. 271 */ 272 private boolean mIsCallSubjectSupported; 273 274 /** 275 * Used only to create mock calls for testing 276 */ 277 @NeededForTesting Call(int state)278 Call(int state) { 279 mTelecommCall = null; 280 mId = ID_PREFIX + Integer.toString(sIdCounter++); 281 setState(state); 282 } 283 Call(android.telecom.Call telecommCall)284 public Call(android.telecom.Call telecommCall) { 285 mTelecommCall = telecommCall; 286 mId = ID_PREFIX + Integer.toString(sIdCounter++); 287 288 updateFromTelecommCall(); 289 mTelecommCall.registerCallback(mTelecomCallCallback); 290 } 291 getTelecommCall()292 public android.telecom.Call getTelecommCall() { 293 return mTelecommCall; 294 } 295 296 /** 297 * @return video settings of the call, null if the call is not a video call. 298 * @see VideoProfile 299 */ getVideoSettings()300 public VideoSettings getVideoSettings() { 301 return mVideoSettings; 302 } 303 update()304 private void update() { 305 Trace.beginSection("Update"); 306 int oldState = getState(); 307 updateFromTelecommCall(); 308 if (oldState != getState() && getState() == Call.State.DISCONNECTED) { 309 CallList.getInstance().onDisconnect(this); 310 } else { 311 CallList.getInstance().onUpdate(this); 312 } 313 Trace.endSection(); 314 } 315 updateFromTelecommCall()316 private void updateFromTelecommCall() { 317 Log.d(this, "updateFromTelecommCall: " + mTelecommCall.toString()); 318 setState(translateState(mTelecommCall.getState())); 319 setDisconnectCause(mTelecommCall.getDetails().getDisconnectCause()); 320 321 if (mTelecommCall.getVideoCall() != null) { 322 if (mVideoCallCallback == null) { 323 mVideoCallCallback = new InCallVideoCallCallback(this); 324 } 325 mTelecommCall.getVideoCall().registerCallback(mVideoCallCallback); 326 } 327 328 mChildCallIds.clear(); 329 for (int i = 0; i < mTelecommCall.getChildren().size(); i++) { 330 mChildCallIds.add( 331 CallList.getInstance().getCallByTelecommCall( 332 mTelecommCall.getChildren().get(i)).getId()); 333 } 334 335 Bundle callExtras = mTelecommCall.getDetails().getExtras(); 336 if (callExtras != null) { 337 // Check for a change in the child address and notify any listeners. 338 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) { 339 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS); 340 341 if (!Objects.equals(childNumber, mChildNumber)) { 342 mChildNumber = childNumber; 343 CallList.getInstance().onChildNumberChange(this); 344 } 345 } 346 347 // Last forwarded number comes in as an array of strings. We want to choose the last 348 // item in the array. The forwarding numbers arrive independently of when the call is 349 // originally set up, so we need to notify the the UI of the change. 350 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) { 351 ArrayList<String> lastForwardedNumbers = 352 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER); 353 354 if (lastForwardedNumbers != null) { 355 String lastForwardedNumber = null; 356 if (!lastForwardedNumbers.isEmpty()) { 357 lastForwardedNumber = lastForwardedNumbers.get( 358 lastForwardedNumbers.size() - 1); 359 } 360 361 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) { 362 mLastForwardedNumber = lastForwardedNumber; 363 CallList.getInstance().onLastForwardedNumberChange(this); 364 } 365 } 366 } 367 368 // Call subject is present in the extras at the start of call, so we do not need to 369 // notify any other listeners of this. 370 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) { 371 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT); 372 if (!Objects.equals(mCallSubject, callSubject)) { 373 mCallSubject = callSubject; 374 } 375 } 376 } 377 378 // If the handle of the call has changed, update state for the call determining if it is an 379 // emergency call. 380 Uri newHandle = mTelecommCall.getDetails().getHandle(); 381 if (!Objects.equals(mHandle, newHandle)) { 382 mHandle = newHandle; 383 updateEmergencyCallState(); 384 } 385 386 // If the phone account handle of the call is set, cache capability bit indicating whether 387 // the phone account supports call subjects. 388 PhoneAccountHandle newPhoneAccountHandle = mTelecommCall.getDetails().getAccountHandle(); 389 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) { 390 mPhoneAccountHandle = newPhoneAccountHandle; 391 392 if (mPhoneAccountHandle != null) { 393 TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager(); 394 PhoneAccount phoneAccount = mgr.getPhoneAccount(mPhoneAccountHandle); 395 if (phoneAccount != null) { 396 mIsCallSubjectSupported = phoneAccount.hasCapabilities( 397 PhoneAccount.CAPABILITY_CALL_SUBJECT); 398 } 399 } 400 } 401 } 402 translateState(int state)403 private static int translateState(int state) { 404 switch (state) { 405 case android.telecom.Call.STATE_NEW: 406 case android.telecom.Call.STATE_CONNECTING: 407 return Call.State.CONNECTING; 408 case android.telecom.Call.STATE_SELECT_PHONE_ACCOUNT: 409 return Call.State.SELECT_PHONE_ACCOUNT; 410 case android.telecom.Call.STATE_DIALING: 411 return Call.State.DIALING; 412 case android.telecom.Call.STATE_RINGING: 413 return Call.State.INCOMING; 414 case android.telecom.Call.STATE_ACTIVE: 415 return Call.State.ACTIVE; 416 case android.telecom.Call.STATE_HOLDING: 417 return Call.State.ONHOLD; 418 case android.telecom.Call.STATE_DISCONNECTED: 419 return Call.State.DISCONNECTED; 420 case android.telecom.Call.STATE_DISCONNECTING: 421 return Call.State.DISCONNECTING; 422 default: 423 return Call.State.INVALID; 424 } 425 } 426 getId()427 public String getId() { 428 return mId; 429 } 430 getNumber()431 public String getNumber() { 432 if (mTelecommCall == null) { 433 return null; 434 } 435 if (mTelecommCall.getDetails().getGatewayInfo() != null) { 436 return mTelecommCall.getDetails().getGatewayInfo() 437 .getOriginalAddress().getSchemeSpecificPart(); 438 } 439 return getHandle() == null ? null : getHandle().getSchemeSpecificPart(); 440 } 441 getHandle()442 public Uri getHandle() { 443 return mTelecommCall == null ? null : mTelecommCall.getDetails().getHandle(); 444 } 445 isEmergencyCall()446 public boolean isEmergencyCall() { 447 return mIsEmergencyCall; 448 } 449 getState()450 public int getState() { 451 if (mTelecommCall != null && mTelecommCall.getParent() != null) { 452 return State.CONFERENCED; 453 } else { 454 return mState; 455 } 456 } 457 setState(int state)458 public void setState(int state) { 459 mState = state; 460 } 461 getNumberPresentation()462 public int getNumberPresentation() { 463 return mTelecommCall == null ? null : mTelecommCall.getDetails().getHandlePresentation(); 464 } 465 getCnapNamePresentation()466 public int getCnapNamePresentation() { 467 return mTelecommCall == null ? null 468 : mTelecommCall.getDetails().getCallerDisplayNamePresentation(); 469 } 470 getCnapName()471 public String getCnapName() { 472 return mTelecommCall == null ? null 473 : getTelecommCall().getDetails().getCallerDisplayName(); 474 } 475 getIntentExtras()476 public Bundle getIntentExtras() { 477 return mTelecommCall == null ? null : mTelecommCall.getDetails().getIntentExtras(); 478 } 479 getExtras()480 public Bundle getExtras() { 481 return mTelecommCall == null ? null : mTelecommCall.getDetails().getExtras(); 482 } 483 484 /** 485 * @return The child number for the call, or {@code null} if none specified. 486 */ getChildNumber()487 public String getChildNumber() { 488 return mChildNumber; 489 } 490 491 /** 492 * @return The last forwarded number for the call, or {@code null} if none specified. 493 */ getLastForwardedNumber()494 public String getLastForwardedNumber() { 495 return mLastForwardedNumber; 496 } 497 498 /** 499 * @return The call subject, or {@code null} if none specified. 500 */ getCallSubject()501 public String getCallSubject() { 502 return mCallSubject; 503 } 504 505 /** 506 * @return {@code true} if the call's phone account supports call subjects, {@code false} 507 * otherwise. 508 */ isCallSubjectSupported()509 public boolean isCallSubjectSupported() { 510 return mIsCallSubjectSupported; 511 } 512 513 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */ getDisconnectCause()514 public DisconnectCause getDisconnectCause() { 515 if (mState == State.DISCONNECTED || mState == State.IDLE) { 516 return mDisconnectCause; 517 } 518 519 return new DisconnectCause(DisconnectCause.UNKNOWN); 520 } 521 setDisconnectCause(DisconnectCause disconnectCause)522 public void setDisconnectCause(DisconnectCause disconnectCause) { 523 mDisconnectCause = disconnectCause; 524 } 525 526 /** Returns the possible text message responses. */ getCannedSmsResponses()527 public List<String> getCannedSmsResponses() { 528 return mTelecommCall.getCannedTextResponses(); 529 } 530 531 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */ can(int capabilities)532 public boolean can(int capabilities) { 533 int supportedCapabilities = mTelecommCall.getDetails().getCallCapabilities(); 534 535 if ((capabilities & android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) { 536 // We allow you to merge if the capabilities allow it or if it is a call with 537 // conferenceable calls. 538 if (mTelecommCall.getConferenceableCalls().isEmpty() && 539 ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE 540 & supportedCapabilities) == 0)) { 541 // Cannot merge calls if there are no calls to merge with. 542 return false; 543 } 544 capabilities &= ~android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE; 545 } 546 return (capabilities == (capabilities & mTelecommCall.getDetails().getCallCapabilities())); 547 } 548 hasProperty(int property)549 public boolean hasProperty(int property) { 550 return mTelecommCall.getDetails().hasProperty(property); 551 } 552 553 /** Gets the time when the call first became active. */ getConnectTimeMillis()554 public long getConnectTimeMillis() { 555 return mTelecommCall.getDetails().getConnectTimeMillis(); 556 } 557 isConferenceCall()558 public boolean isConferenceCall() { 559 return mTelecommCall.getDetails().hasProperty( 560 android.telecom.Call.Details.PROPERTY_CONFERENCE); 561 } 562 getGatewayInfo()563 public GatewayInfo getGatewayInfo() { 564 return mTelecommCall == null ? null : mTelecommCall.getDetails().getGatewayInfo(); 565 } 566 getAccountHandle()567 public PhoneAccountHandle getAccountHandle() { 568 return mTelecommCall == null ? null : mTelecommCall.getDetails().getAccountHandle(); 569 } 570 getVideoCall()571 public VideoCall getVideoCall() { 572 return mTelecommCall == null ? null : mTelecommCall.getVideoCall(); 573 } 574 getChildCallIds()575 public List<String> getChildCallIds() { 576 return mChildCallIds; 577 } 578 getParentId()579 public String getParentId() { 580 android.telecom.Call parentCall = mTelecommCall.getParent(); 581 if (parentCall != null) { 582 return CallList.getInstance().getCallByTelecommCall(parentCall).getId(); 583 } 584 return null; 585 } 586 getVideoState()587 public int getVideoState() { 588 return mTelecommCall.getDetails().getVideoState(); 589 } 590 isVideoCall(Context context)591 public boolean isVideoCall(Context context) { 592 return CallUtil.isVideoEnabled(context) && 593 CallUtils.isVideoCall(getVideoState()); 594 } 595 596 /** 597 * This method is called when we request for a video upgrade or downgrade. This handles the 598 * session modification state RECEIVED_UPGRADE_TO_VIDEO_REQUEST and sets the video state we 599 * want to upgrade/downgrade to. 600 */ setSessionModificationTo(int videoState)601 public void setSessionModificationTo(int videoState) { 602 Log.d(this, "setSessionModificationTo - video state= " + videoState); 603 if (videoState == getVideoState()) { 604 mSessionModificationState = Call.SessionModificationState.NO_REQUEST; 605 Log.w(this,"setSessionModificationTo - Clearing session modification state"); 606 } else { 607 mSessionModificationState = 608 Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; 609 setModifyToVideoState(videoState); 610 CallList.getInstance().onUpgradeToVideo(this); 611 } 612 613 Log.d(this, "setSessionModificationTo - mSessionModificationState=" 614 + mSessionModificationState + " video state= " + videoState); 615 update(); 616 } 617 618 /** 619 * This method is called to handle any other session modification states other than 620 * RECEIVED_UPGRADE_TO_VIDEO_REQUEST. We set the modification state and reset the video state 621 * when an upgrade request has been completed or failed. 622 */ setSessionModificationState(int state)623 public void setSessionModificationState(int state) { 624 if (state == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { 625 Log.e(this, 626 "setSessionModificationState not valid for RECEIVED_UPGRADE_TO_VIDEO_REQUEST"); 627 return; 628 } 629 630 boolean hasChanged = mSessionModificationState != state; 631 mSessionModificationState = state; 632 Log.d(this, "setSessionModificationState " + state + " mSessionModificationState=" 633 + mSessionModificationState); 634 if (hasChanged) { 635 CallList.getInstance().onSessionModificationStateChange(this, state); 636 } 637 } 638 639 /** 640 * Determines if the call handle is an emergency number or not and caches the result to avoid 641 * repeated calls to isEmergencyNumber. 642 */ updateEmergencyCallState()643 private void updateEmergencyCallState() { 644 Uri handle = mTelecommCall.getDetails().getHandle(); 645 mIsEmergencyCall = PhoneNumberUtils.isEmergencyNumber( 646 handle == null ? "" : handle.getSchemeSpecificPart()); 647 } 648 setModifyToVideoState(int newVideoState)649 private void setModifyToVideoState(int newVideoState) { 650 mModifyToVideoState = newVideoState; 651 } 652 getModifyToVideoState()653 public int getModifyToVideoState() { 654 return mModifyToVideoState; 655 } 656 areSame(Call call1, Call call2)657 public static boolean areSame(Call call1, Call call2) { 658 if (call1 == null && call2 == null) { 659 return true; 660 } else if (call1 == null || call2 == null) { 661 return false; 662 } 663 664 // otherwise compare call Ids 665 return call1.getId().equals(call2.getId()); 666 } 667 areSameNumber(Call call1, Call call2)668 public static boolean areSameNumber(Call call1, Call call2) { 669 if (call1 == null && call2 == null) { 670 return true; 671 } else if (call1 == null || call2 == null) { 672 return false; 673 } 674 675 // otherwise compare call Numbers 676 return TextUtils.equals(call1.getNumber(), call2.getNumber()); 677 } 678 getSessionModificationState()679 public int getSessionModificationState() { 680 return mSessionModificationState; 681 } 682 683 @Override toString()684 public String toString() { 685 if (mTelecommCall == null) { 686 // This should happen only in testing since otherwise we would never have a null 687 // Telecom call. 688 return String.valueOf(mId); 689 } 690 691 return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, conferenceable:%s, " + 692 "videoState:%s, mSessionModificationState:%d, VideoSettings:%s]", 693 mId, 694 State.toString(getState()), 695 android.telecom.Call.Details 696 .capabilitiesToString(mTelecommCall.getDetails().getCallCapabilities()), 697 mChildCallIds, 698 getParentId(), 699 this.mTelecommCall.getConferenceableCalls(), 700 VideoProfile.videoStateToString(mTelecommCall.getDetails().getVideoState()), 701 mSessionModificationState, 702 getVideoSettings()); 703 } 704 toSimpleString()705 public String toSimpleString() { 706 return super.toString(); 707 } 708 } 709