Ensure null ConnectionService references don't crash phone.
- Modified Call logic to remove precondition asserts for mConnectionService
in favor of null check and error logging.
- Modify rejectCallAndLog to skip calling reject on the incoming call if it
is already destroyed. This is due to a call which was ended before the
call filtering completes.
Bug: 31236443
Change-Id: I0f93c65697a1b8e697a49118d642ecffe3dea130
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index f536561..e225e8a 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1328,8 +1328,6 @@
*/
@VisibleForTesting
public void answer(int videoState) {
- Preconditions.checkNotNull(mConnectionService);
-
// Check to verify that the call is still in the ringing state. A call can change states
// between the time the user hits 'answer' and Telecom receives the command.
if (isRinging("answer")) {
@@ -1342,7 +1340,12 @@
// that it will work. Instead, we wait until confirmation from the connectino service
// that the call is in a non-STATE_RINGING state before changing the UI. See
// {@link ConnectionServiceAdapter#setActive} and other set* methods.
- mConnectionService.answer(this, videoState);
+ if (mConnectionService != null) {
+ mConnectionService.answer(this, videoState);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "answer call failed due to null CS callId=%s", getId());
+ }
Log.event(this, Log.Events.REQUEST_ACCEPT);
}
}
@@ -1355,16 +1358,20 @@
*/
@VisibleForTesting
public void reject(boolean rejectWithMessage, String textMessage) {
- Preconditions.checkNotNull(mConnectionService);
-
// Check to verify that the call is still in the ringing state. A call can change states
// between the time the user hits 'reject' and Telecomm receives the command.
if (isRinging("reject")) {
// Ensure video state history tracks video state at time of rejection.
mVideoStateHistory |= mVideoState;
- mConnectionService.reject(this, rejectWithMessage, textMessage);
+ if (mConnectionService != null) {
+ mConnectionService.reject(this, rejectWithMessage, textMessage);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "reject call failed due to null CS callId=%s", getId());
+ }
Log.event(this, Log.Events.REQUEST_REJECT);
+
}
}
@@ -1372,10 +1379,13 @@
* Puts the call on hold if it is currently active.
*/
void hold() {
- Preconditions.checkNotNull(mConnectionService);
-
if (mState == CallState.ACTIVE) {
- mConnectionService.hold(this);
+ if (mConnectionService != null) {
+ mConnectionService.hold(this);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "hold call failed due to null CS callId=%s", getId());
+ }
Log.event(this, Log.Events.REQUEST_HOLD);
}
}
@@ -1384,10 +1394,13 @@
* Releases the call from hold if it is currently active.
*/
void unhold() {
- Preconditions.checkNotNull(mConnectionService);
-
if (mState == CallState.ON_HOLD) {
- mConnectionService.unhold(this);
+ if (mConnectionService != null) {
+ mConnectionService.unhold(this);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "unhold call failed due to null CS callId=%s", getId());
+ }
Log.event(this, Log.Events.REQUEST_UNHOLD);
}
}
@@ -1441,7 +1454,12 @@
// If the change originated from an InCallService, notify the connection service.
if (source == SOURCE_INCALL_SERVICE) {
- mConnectionService.onExtrasChanged(this, mExtras);
+ if (mConnectionService != null) {
+ mConnectionService.onExtrasChanged(this, mExtras);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "putExtras failed due to null CS callId=%s", getId());
+ }
}
}
@@ -1471,7 +1489,12 @@
// If the change originated from an InCallService, notify the connection service.
if (source == SOURCE_INCALL_SERVICE) {
- mConnectionService.onExtrasChanged(this, mExtras);
+ if (mConnectionService != null) {
+ mConnectionService.onExtrasChanged(this, mExtras);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "removeExtras failed due to null CS callId=%s", getId());
+ }
}
}
@@ -1512,7 +1535,12 @@
}
void postDialContinue(boolean proceed) {
- mConnectionService.onPostDialContinue(this, proceed);
+ if (mConnectionService != null) {
+ mConnectionService.onPostDialContinue(this, proceed);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "postDialContinue failed due to null CS callId=%s", getId());
+ }
}
void conferenceWith(Call otherCall) {
@@ -1614,7 +1642,12 @@
* @param extras Associated extras.
*/
public void sendCallEvent(String event, Bundle extras) {
- mConnectionService.sendCallEvent(this, event, extras);
+ if (mConnectionService != null) {
+ mConnectionService.sendCallEvent(this, event, extras);
+ } else {
+ Log.e(this, new NullPointerException(),
+ "sendCallEvent failed due to null CS callId=%s", getId());
+ }
}
void setParentCall(Call parentCall) {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index b60f70b..d70daec 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1693,7 +1693,16 @@
* @param incomingCall Incoming call that has been rejected
*/
private void rejectCallAndLog(Call incomingCall) {
- incomingCall.reject(false, null);
+ if (incomingCall.getConnectionService() != null) {
+ // Only reject the call if it has not already been destroyed. If a call ends while
+ // incoming call filtering is taking place, it is possible that the call has already
+ // been destroyed, and as such it will be impossible to send the reject to the
+ // associated ConnectionService.
+ incomingCall.reject(false, null);
+ } else {
+ Log.i(this, "rejectCallAndLog - call already destroyed.");
+ }
+
// Since the call was not added to the list of calls, we have to call the missed
// call notifier and the call logger manually.
// Do we need missed call notification for direct to Voicemail calls?