From 165ee7501fb1a13b5dcde1a9986426cfa0c93f4d Mon Sep 17 00:00:00 2001 From: Tyler Gunn Date: Tue, 6 Sep 2016 13:40:10 -0700 Subject: 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 --- src/com/android/server/telecom/Call.java | 65 ++++++++++++++++++------ src/com/android/server/telecom/CallsManager.java | 11 +++- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java index f5365617..e225e8af 100644 --- a/src/com/android/server/telecom/Call.java +++ b/src/com/android/server/telecom/Call.java @@ -1328,8 +1328,6 @@ public class Call implements CreateConnectionResponse { */ @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 @@ public class Call implements CreateConnectionResponse { // 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 @@ public class Call implements CreateConnectionResponse { */ @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 @@ public class Call implements CreateConnectionResponse { * 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 @@ public class Call implements CreateConnectionResponse { * 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 @@ public class Call implements CreateConnectionResponse { // 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 @@ public class Call implements CreateConnectionResponse { // 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 @@ public class Call implements CreateConnectionResponse { } 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 @@ public class Call implements CreateConnectionResponse { * @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 b60f70b9..d70daece 100644 --- a/src/com/android/server/telecom/CallsManager.java +++ b/src/com/android/server/telecom/CallsManager.java @@ -1693,7 +1693,16 @@ public class CallsManager extends Call.ListenerBase * @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? -- cgit v1.2.3