Loading src/com/android/server/telecom/Call.java +10 −0 Original line number Diff line number Diff line Loading @@ -399,6 +399,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, private boolean mIsConference = false; private boolean mHadChildren = false; private final boolean mShouldAttachToExistingConnection; private Call mParentCall = null; Loading Loading @@ -1011,6 +1013,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, return mIsConference; } /** * @return {@code true} if this call had children at some point, {@code false} otherwise. */ public boolean hadChildren() { return mHadChildren; } public Uri getHandle() { return mHandle; } Loading Loading @@ -2476,6 +2485,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, private void addChildCall(Call call) { if (!mChildCalls.contains(call)) { mHadChildren = true; // Set the pseudo-active call to the latest child added to the conference. // See definition of mConferenceLevelActiveCall for more detail. mConferenceLevelActiveCall = call; Loading src/com/android/server/telecom/CallLogManager.java +27 −18 Original line number Diff line number Diff line Loading @@ -182,44 +182,53 @@ public final class CallLogManager extends CallsManagerListenerBase { /** * Log newly disconnected calls only if all of below conditions are met: * 1) Call was NOT in the "choose account" phase when disconnected * 2) Call is NOT a conference call * 3) Call is NOT simulating a single party conference. * 4) Call was NOT explicitly canceled, except for disconnecting from a conference. * 5) Call is NOT an external call * 6) Call is NOT disconnected because of merging into a conference. * 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it * Call was NOT in the "choose account" phase when disconnected * Call is NOT a conference call which had children (unless it was remotely hosted). * Call is NOT a child call from a conference which was remotely hosted. * Call is NOT simulating a single party conference. * Call was NOT explicitly canceled, except for disconnecting from a conference. * Call is NOT an external call * Call is NOT disconnected because of merging into a conference. * Call is NOT a self-managed call OR call is a self-managed call which has indicated it * should be logged in its PhoneAccount */ private boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) { // 1) "Choose account" phase when disconnected @VisibleForTesting public boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) { // "Choose account" phase when disconnected if (oldState == CallState.SELECT_PHONE_ACCOUNT) { return false; } // 2) A conference call if (call.isConference()) { // A conference call which had children should not be logged, unless it was remotely hosted. if (call.isConference() && call.hadChildren() && !call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) { return false; } // A child call of a conference which was remotely hosted; these didn't originate on this // device and should not be logged. if (call.getParentCall() != null && call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) { return false; } DisconnectCause cause = call.getDisconnectCause(); if (isCallCanceled) { // 3) No log when disconnecting to simulate a single party conference. // No log when disconnecting to simulate a single party conference. if (cause != null && DisconnectCause.REASON_EMULATING_SINGLE_CALL.equals(cause.getReason())) { return false; } // 4) Explicitly canceled // Explicitly canceled // Conference children connections only have CAPABILITY_DISCONNECT_FROM_CONFERENCE. // Log them when they are disconnected from conference. return Connection.can(call.getConnectionCapabilities(), Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE); } // 5) An external call // An external call if (call.isExternalCall()) { return false; } // 6) Call merged into conferences. // Call merged into conferences. if (cause != null && android.telephony.DisconnectCause.toString( android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY) .equals(cause.getReason())) { Loading @@ -229,7 +238,7 @@ public final class CallLogManager extends CallsManagerListenerBase { boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged() && (call.getHandoverState() == HandoverState.HANDOVER_NONE || call.getHandoverState() == HandoverState.HANDOVER_COMPLETE); // 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it // Call is NOT a self-managed call OR call is a self-managed call which has indicated it // should be logged in its PhoneAccount return !call.isSelfManaged() || shouldCallSelfManagedLogged; } Loading src/com/android/server/telecom/CallsManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -2814,6 +2814,8 @@ public class CallsManager extends Call.ListenerBase setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()), "new conference call"); call.setHandle(parcelableConference.getHandle(), parcelableConference.getHandlePresentation()); call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); call.setConnectionProperties(parcelableConference.getConnectionProperties()); call.setVideoState(parcelableConference.getVideoState()); Loading tests/src/com/android/server/telecom/tests/CallLogManagerTest.java +135 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; Loading Loading @@ -48,6 +49,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.telecom.Connection; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; Loading Loading @@ -781,6 +783,136 @@ public class CallLogManagerTest extends TelecomTestCase { assertEquals(TEST_ISO_2, resultIso2); } @SmallTest @Test public void testLogConferenceWithNoChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(false); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testDoNotLogConferenceWithChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(true); assertFalse(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testLogRemotelyHostedConferenceWithChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(true); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testLogRemotelyHostedConferenceWithNoChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(false); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testDoNotLogChildOfRemotelyHostedConference() { Call fakeConfCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeConfCall.hadChildren()).thenReturn(true); when(fakeConfCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); Call fakeChild = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode false, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeChild.hadChildren()).thenReturn(false); when(fakeChild.getParentCall()).thenReturn(fakeConfCall); when(fakeChild.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertFalse(mCallLogManager.shouldLogDisconnectedCall(fakeChild, CallState.DISCONNECTED, false /* isCanceled */)); } private ArgumentCaptor<CountryListener> verifyCountryIso(CountryDetector mockDetector, String resultIso) { ArgumentCaptor<CountryListener> captor = ArgumentCaptor.forClass(CountryListener.class); Loading Loading @@ -862,6 +994,9 @@ public class CallLogManagerTest extends TelecomTestCase { when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage); when(fakeCall.isEmergencyCall()).thenReturn( phoneAccountHandle.equals(EMERGENCY_ACCT_HANDLE)); when(fakeCall.getParentCall()).thenReturn(null); when(fakeCall.hadChildren()).thenReturn(true); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(false); return fakeCall; } Loading tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java +5 −1 Original line number Diff line number Diff line Loading @@ -670,7 +670,11 @@ public class ConnectionServiceFixture implements TestFixture<IConnectionService> c.connectTimeMillis, c.connectElapsedTimeMillis, c.statusHints, c.extras); c.extras, null, 0, null, 0); } private ParcelableConnection parcelable(ConnectionInfo c) { Loading Loading
src/com/android/server/telecom/Call.java +10 −0 Original line number Diff line number Diff line Loading @@ -399,6 +399,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, private boolean mIsConference = false; private boolean mHadChildren = false; private final boolean mShouldAttachToExistingConnection; private Call mParentCall = null; Loading Loading @@ -1011,6 +1013,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, return mIsConference; } /** * @return {@code true} if this call had children at some point, {@code false} otherwise. */ public boolean hadChildren() { return mHadChildren; } public Uri getHandle() { return mHandle; } Loading Loading @@ -2476,6 +2485,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, private void addChildCall(Call call) { if (!mChildCalls.contains(call)) { mHadChildren = true; // Set the pseudo-active call to the latest child added to the conference. // See definition of mConferenceLevelActiveCall for more detail. mConferenceLevelActiveCall = call; Loading
src/com/android/server/telecom/CallLogManager.java +27 −18 Original line number Diff line number Diff line Loading @@ -182,44 +182,53 @@ public final class CallLogManager extends CallsManagerListenerBase { /** * Log newly disconnected calls only if all of below conditions are met: * 1) Call was NOT in the "choose account" phase when disconnected * 2) Call is NOT a conference call * 3) Call is NOT simulating a single party conference. * 4) Call was NOT explicitly canceled, except for disconnecting from a conference. * 5) Call is NOT an external call * 6) Call is NOT disconnected because of merging into a conference. * 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it * Call was NOT in the "choose account" phase when disconnected * Call is NOT a conference call which had children (unless it was remotely hosted). * Call is NOT a child call from a conference which was remotely hosted. * Call is NOT simulating a single party conference. * Call was NOT explicitly canceled, except for disconnecting from a conference. * Call is NOT an external call * Call is NOT disconnected because of merging into a conference. * Call is NOT a self-managed call OR call is a self-managed call which has indicated it * should be logged in its PhoneAccount */ private boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) { // 1) "Choose account" phase when disconnected @VisibleForTesting public boolean shouldLogDisconnectedCall(Call call, int oldState, boolean isCallCanceled) { // "Choose account" phase when disconnected if (oldState == CallState.SELECT_PHONE_ACCOUNT) { return false; } // 2) A conference call if (call.isConference()) { // A conference call which had children should not be logged, unless it was remotely hosted. if (call.isConference() && call.hadChildren() && !call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) { return false; } // A child call of a conference which was remotely hosted; these didn't originate on this // device and should not be logged. if (call.getParentCall() != null && call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) { return false; } DisconnectCause cause = call.getDisconnectCause(); if (isCallCanceled) { // 3) No log when disconnecting to simulate a single party conference. // No log when disconnecting to simulate a single party conference. if (cause != null && DisconnectCause.REASON_EMULATING_SINGLE_CALL.equals(cause.getReason())) { return false; } // 4) Explicitly canceled // Explicitly canceled // Conference children connections only have CAPABILITY_DISCONNECT_FROM_CONFERENCE. // Log them when they are disconnected from conference. return Connection.can(call.getConnectionCapabilities(), Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE); } // 5) An external call // An external call if (call.isExternalCall()) { return false; } // 6) Call merged into conferences. // Call merged into conferences. if (cause != null && android.telephony.DisconnectCause.toString( android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY) .equals(cause.getReason())) { Loading @@ -229,7 +238,7 @@ public final class CallLogManager extends CallsManagerListenerBase { boolean shouldCallSelfManagedLogged = call.isLoggedSelfManaged() && (call.getHandoverState() == HandoverState.HANDOVER_NONE || call.getHandoverState() == HandoverState.HANDOVER_COMPLETE); // 7) Call is NOT a self-managed call OR call is a self-managed call which has indicated it // Call is NOT a self-managed call OR call is a self-managed call which has indicated it // should be logged in its PhoneAccount return !call.isSelfManaged() || shouldCallSelfManagedLogged; } Loading
src/com/android/server/telecom/CallsManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -2814,6 +2814,8 @@ public class CallsManager extends Call.ListenerBase setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()), "new conference call"); call.setHandle(parcelableConference.getHandle(), parcelableConference.getHandlePresentation()); call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); call.setConnectionProperties(parcelableConference.getConnectionProperties()); call.setVideoState(parcelableConference.getVideoState()); Loading
tests/src/com/android/server/telecom/tests/CallLogManagerTest.java +135 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; Loading Loading @@ -48,6 +49,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.telecom.Connection; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; Loading Loading @@ -781,6 +783,136 @@ public class CallLogManagerTest extends TelecomTestCase { assertEquals(TEST_ISO_2, resultIso2); } @SmallTest @Test public void testLogConferenceWithNoChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(false); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testDoNotLogConferenceWithChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(true); assertFalse(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testLogRemotelyHostedConferenceWithChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(true); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testLogRemotelyHostedConferenceWithNoChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeCall.hadChildren()).thenReturn(false); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertTrue(mCallLogManager.shouldLogDisconnectedCall(fakeCall, CallState.DISCONNECTED, false /* isCanceled */)); } @SmallTest @Test public void testDoNotLogChildOfRemotelyHostedConference() { Call fakeConfCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode true, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeConfCall.hadChildren()).thenReturn(true); when(fakeConfCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); Call fakeChild = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode false, // isConference true, // isIncoming 1L, // creationTimeMillis 1000L, // ageMillis TEL_PHONEHANDLE, // callHandle mDefaultAccountHandle, // phoneAccountHandle NO_VIDEO_STATE, // callVideoState POST_DIAL_STRING, // postDialDigits VIA_NUMBER_STRING, // viaNumber UserHandle.of(CURRENT_USER_ID) ); when(fakeChild.hadChildren()).thenReturn(false); when(fakeChild.getParentCall()).thenReturn(fakeConfCall); when(fakeChild.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(true); assertFalse(mCallLogManager.shouldLogDisconnectedCall(fakeChild, CallState.DISCONNECTED, false /* isCanceled */)); } private ArgumentCaptor<CountryListener> verifyCountryIso(CountryDetector mockDetector, String resultIso) { ArgumentCaptor<CountryListener> captor = ArgumentCaptor.forClass(CountryListener.class); Loading Loading @@ -862,6 +994,9 @@ public class CallLogManagerTest extends TelecomTestCase { when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage); when(fakeCall.isEmergencyCall()).thenReturn( phoneAccountHandle.equals(EMERGENCY_ACCT_HANDLE)); when(fakeCall.getParentCall()).thenReturn(null); when(fakeCall.hadChildren()).thenReturn(true); when(fakeCall.hasProperty(eq(Connection.PROPERTY_REMOTELY_HOSTED))).thenReturn(false); return fakeCall; } Loading
tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java +5 −1 Original line number Diff line number Diff line Loading @@ -670,7 +670,11 @@ public class ConnectionServiceFixture implements TestFixture<IConnectionService> c.connectTimeMillis, c.connectElapsedTimeMillis, c.statusHints, c.extras); c.extras, null, 0, null, 0); } private ParcelableConnection parcelable(ConnectionInfo c) { Loading