Merge "Change getAgeMillis to use elapsed system time instead of wall-clock time."
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index c69a3e0..62f5eaa 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.provider.ContactsContract.Contacts;
 import android.telecom.CallAudioState;
@@ -235,17 +236,38 @@
     private String mViaNumber = "";
 
     /**
-     * The time this call was created. Beyond logging and such, may also be used for bookkeeping
-     * and specifically for marking certain call attempts as failed attempts.
+     * The wall clock time this call was created. Beyond logging and such, may also be used for
+     * bookkeeping and specifically for marking certain call attempts as failed attempts.
+     * Note: This timestamp should NOT be used for calculating call duration.
      */
-    private long mCreationTimeMillis = System.currentTimeMillis();
+    private long mCreationTimeMillis;
 
     /** The time this call was made active. */
     private long mConnectTimeMillis = 0;
 
-    /** The time this call was disconnected. */
+    /**
+     * The time, in millis, since boot when this call was connected.  This should ONLY be used when
+     * calculating the duration of the call.
+     *
+     * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the
+     * elapsed time since the device was booted.  Changes to the system clock (e.g. due to NITZ
+     * time sync, time zone changes user initiated clock changes) would cause a duration calculated
+     * based on {@link #mConnectTimeMillis} to change based on the delta in the time.
+     * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do
+     * not impact the call duration.
+     */
+    private long mConnectElapsedTimeMillis = 0;
+
+    /** The wall clock time this call was disconnected. */
     private long mDisconnectTimeMillis = 0;
 
+    /**
+     * The elapsed time since boot when this call was disconnected.  Recorded as the
+     * {@link SystemClock#elapsedRealtime()}.  This ensures that the call duration is not impacted
+     * by changes in the wall time clock.
+     */
+    private long mDisconnectElapsedTimeMillis = 0;
+
     /** The gateway information associated with this call. This stores the original call handle
      * that the user is attempting to connect to via the gateway, the actual handle to dial in
      * order to connect the call via the gateway, as well as the package name of the gateway
@@ -369,6 +391,7 @@
     private final ConnectionServiceRepository mRepository;
     private final Context mContext;
     private final CallsManager mCallsManager;
+    private final ClockProxy mClockProxy;
     private final TelecomSystem.SyncRoot mLock;
     private final String mId;
     private String mConnectionId;
@@ -447,20 +470,19 @@
 
     /**
      * Persists the specified parameters and initializes the new instance.
-     *
-     * @param context The context.
+     *  @param context The context.
      * @param repository The connection service repository.
      * @param handle The handle to dial.
      * @param gatewayInfo Gateway information to use for the call.
      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
-     *         This account must be one that was registered with the
-     *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
+*         This account must be one that was registered with the
+*         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
-     *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
+*         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
-     *         or CALL_DIRECTION_UNKNOWN.
+*         or CALL_DIRECTION_UNKNOWN.
      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
-     *         connection, regardless of whether it's incoming or outgoing.
+     * @param clockProxy
      */
     public Call(
             String callId,
@@ -477,7 +499,8 @@
             PhoneAccountHandle targetPhoneAccountHandle,
             int callDirection,
             boolean shouldAttachToExistingConnection,
-            boolean isConference) {
+            boolean isConference,
+            ClockProxy clockProxy) {
         mId = callId;
         mConnectionId = callId;
         mState = isConference ? CallState.ACTIVE : CallState.NEW;
@@ -498,26 +521,27 @@
                 || callDirection == CALL_DIRECTION_INCOMING;
         maybeLoadCannedSmsResponses();
         mAnalytics = new Analytics.CallInfo();
-
+        mClockProxy = clockProxy;
+        mCreationTimeMillis = mClockProxy.currentTimeMillis();
     }
 
     /**
      * Persists the specified parameters and initializes the new instance.
-     *
-     * @param context The context.
+     *  @param context The context.
      * @param repository The connection service repository.
      * @param handle The handle to dial.
      * @param gatewayInfo Gateway information to use for the call.
      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
-     *         This account must be one that was registered with the
-     *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
+*         This account must be one that was registered with the
+*         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
-     *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
+*         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
-     *         or CALL_DIRECTION_UNKNOWN
+*         or CALL_DIRECTION_UNKNOWN
      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
-     *         connection, regardless of whether it's incoming or outgoing.
+*         connection, regardless of whether it's incoming or outgoing.
      * @param connectTimeMillis The connection time of the call.
+     * @param clockProxy
      */
     Call(
             String callId,
@@ -535,13 +559,16 @@
             int callDirection,
             boolean shouldAttachToExistingConnection,
             boolean isConference,
-            long connectTimeMillis) {
+            long connectTimeMillis,
+            long connectElapsedTimeMillis,
+            ClockProxy clockProxy) {
         this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
                 callerInfoAsyncQueryFactory, phoneNumberUtilsAdapter, handle, gatewayInfo,
                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
-                shouldAttachToExistingConnection, isConference);
+                shouldAttachToExistingConnection, isConference, clockProxy);
 
         mConnectTimeMillis = connectTimeMillis;
+        mConnectElapsedTimeMillis = connectElapsedTimeMillis;
         mAnalytics.setCallStartTime(connectTimeMillis);
     }
 
@@ -747,7 +774,8 @@
                     // We check to see if mConnectTime is already set to prevent the
                     // call from resetting active time when it goes in and out of
                     // ACTIVE/ON_HOLD
-                    mConnectTimeMillis = System.currentTimeMillis();
+                    mConnectTimeMillis = mClockProxy.currentTimeMillis();
+                    mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
                     mAnalytics.setCallStartTime(mConnectTimeMillis);
                 }
 
@@ -759,8 +787,10 @@
 
                 // We're clearly not disconnected, so reset the disconnected time.
                 mDisconnectTimeMillis = 0;
+                mDisconnectElapsedTimeMillis = 0;
             } else if (mState == CallState.DISCONNECTED) {
-                mDisconnectTimeMillis = System.currentTimeMillis();
+                mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
+                mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
                 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
                 setLocallyDisconnecting(false);
                 fixParentAfterDisconnect();
@@ -1092,9 +1122,11 @@
     }
 
     /**
+     * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
+     * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
+     * change due to clock changes).
      * @return The "age" of this call object in milliseconds, which typically also represents the
-     *     period since this call was added to the set pending outgoing calls, see
-     *     mCreationTimeMillis.
+     *     period since this call was added to the set pending outgoing calls.
      */
     @VisibleForTesting
     public long getAgeMillis() {
@@ -1103,16 +1135,16 @@
                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
             // Rejected and missed calls have no age. They're immortal!!
             return 0;
-        } else if (mConnectTimeMillis == 0) {
+        } else if (mConnectElapsedTimeMillis == 0) {
             // Age is measured in the amount of time the call was active. A zero connect time
             // indicates that we never went active, so return 0 for the age.
             return 0;
-        } else if (mDisconnectTimeMillis == 0) {
+        } else if (mDisconnectElapsedTimeMillis == 0) {
             // We connected, but have not yet disconnected
-            return System.currentTimeMillis() - mConnectTimeMillis;
+            return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
         }
 
-        return mDisconnectTimeMillis - mConnectTimeMillis;
+        return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
     }
 
     /**
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 06ccd4e..4e7216b 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -259,6 +259,7 @@
     private final Timeouts.Adapter mTimeoutsAdapter;
     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
     private final NotificationManager mNotificationManager;
+    private final ClockProxy mClockProxy;
     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
     /* Handler tied to thread in which CallManager was initialized. */
@@ -308,7 +309,8 @@
             AsyncRingtonePlayer asyncRingtonePlayer,
             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             InterruptionFilterProxy interruptionFilterProxy,
-            EmergencyCallHelper emergencyCallHelper) {
+            EmergencyCallHelper emergencyCallHelper,
+            ClockProxy clockProxy) {
         mContext = context;
         mLock = lock;
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -375,6 +377,7 @@
         mConnectionServiceRepository =
                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
+        mClockProxy = clockProxy;
 
         mListeners.add(mInCallWakeLockController);
         mListeners.add(statusBarNotifier);
@@ -823,8 +826,8 @@
                 phoneAccountHandle,
                 Call.CALL_DIRECTION_INCOMING /* callDirection */,
                 false /* forceAttachToExistingConnection */,
-                false /* isConference */
-        );
+                false, /* isConference */
+                mClockProxy);
 
         // Ensure new calls related to self-managed calls/connections are set as such.  This will
         // be overridden when the actual connection is returned in startCreateConnection, however
@@ -907,8 +910,8 @@
                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
                 // to the existing connection instead of trying to create a new one.
                 true /* forceAttachToExistingConnection */,
-                false /* isConference */
-        );
+                false, /* isConference */
+                mClockProxy);
         call.initAnalytics();
 
         setIntentExtrasAndStartTime(call, extras);
@@ -987,8 +990,8 @@
                     null /* phoneAccountHandle */,
                     Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                     false /* forceAttachToExistingConnection */,
-                    false /* isConference */
-            );
+                    false, /* isConference */
+                    mClockProxy);
             call.initAnalytics();
 
             // Ensure new calls related to self-managed calls/connections are set as such.  This
@@ -1946,6 +1949,10 @@
                 parcelableConference.getConnectTimeMillis() ==
                         Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
                         parcelableConference.getConnectTimeMillis();
+        long connectElapsedTime =
+                parcelableConference.getConnectElapsedTimeMillis() ==
+                        Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
+                        parcelableConference.getConnectElapsedTimeMillis();
 
         Call call = new Call(
                 callId,
@@ -1963,7 +1970,9 @@
                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
                 false /* forceAttachToExistingConnection */,
                 true /* isConference */,
-                connectTime);
+                connectTime,
+                connectElapsedTime,
+                mClockProxy);
 
         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
                 "new conference call");
@@ -2507,7 +2516,9 @@
                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
                 false /* forceAttachToExistingConnection */,
                 isDowngradedConference /* isConference */,
-                connection.getConnectTimeMillis() /* connectTimeMillis */);
+                connection.getConnectTimeMillis() /* connectTimeMillis */,
+                connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */
+                mClockProxy);
 
         call.initAnalytics();
         call.getAnalytics().setCreatedFromExistingConnection(true);
diff --git a/src/com/android/server/telecom/ClockProxy.java b/src/com/android/server/telecom/ClockProxy.java
new file mode 100644
index 0000000..6f92f94
--- /dev/null
+++ b/src/com/android/server/telecom/ClockProxy.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.os.SystemClock;
+
+/**
+ * Defines common clock functionality used in Telecom.  Provided to make unit testing of clock
+ * operations testable.
+ */
+public interface ClockProxy {
+    /**
+     * Returns the current wall-clock time of the system, as typically returned by
+     * {@link System#currentTimeMillis()}.
+     * @return The current wall-clock time.
+     */
+    long currentTimeMillis();
+
+    /**
+     * Returns the elapsed time since boot of the system, as typically returned by
+     * {@link SystemClock#elapsedRealtime()}.
+     * @return the current elapsed real time.
+     */
+    long elapsedRealtime();
+}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 9c34ef5..59b194f 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -22,7 +22,6 @@
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.ui.IncomingCallNotifier;
-import com.android.server.telecom.ui.IncomingCallNotifier.IncomingCallNotifierFactory;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
 import com.android.server.telecom.BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory;
 import com.android.server.telecom.CallAudioManager.AudioServiceFactory;
@@ -190,7 +189,8 @@
             AsyncRingtonePlayer asyncRingtonePlayer,
             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             InterruptionFilterProxy interruptionFilterProxy,
-            IncomingCallNotifier incomingCallNotifier) {
+            IncomingCallNotifier incomingCallNotifier,
+            ClockProxy clockProxy) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
         DefaultDialerManagerAdapter defaultDialerAdapter =
@@ -255,7 +255,8 @@
                 asyncRingtonePlayer,
                 phoneNumberUtilsAdapter,
                 interruptionFilterProxy,
-                emergencyCallHelper);
+                emergencyCallHelper,
+                clockProxy);
 
         mIncomingCallNotifier = incomingCallNotifier;
         incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index a2ca9cb..e525701 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -25,9 +25,9 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.service.notification.ZenModeConfig;
 import android.telecom.Log;
-import android.telecom.PhoneAccountHandle;
 
 import com.android.internal.telephony.CallerInfoAsyncQuery;
 import com.android.server.telecom.AsyncRingtonePlayer;
@@ -35,6 +35,7 @@
 import com.android.server.telecom.BluetoothPhoneServiceImpl;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -185,8 +186,18 @@
                                     return null;
                                 }
                             },
-                            new IncomingCallNotifier(context)
-                    ));
+                            new IncomingCallNotifier(context),
+                            new ClockProxy() {
+                                @Override
+                                public long currentTimeMillis() {
+                                    return System.currentTimeMillis();
+                                }
+
+                                @Override
+                                public long elapsedRealtime() {
+                                    return SystemClock.elapsedRealtime();
+                                }
+                            }));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
             context.startService(new Intent(context, BluetoothPhoneService.class));
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index ce774a7..905a15e 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -82,11 +82,22 @@
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+        assertEquals(TEST_CONNECT_TIME,
+                mInCallServiceFixtureX.getCall(ids.mCallId).getConnectTimeMillis());
+        assertEquals(TEST_CONNECT_TIME,
+                mInCallServiceFixtureY.getCall(ids.mCallId).getConnectTimeMillis());
+        assertEquals(TEST_CREATE_TIME,
+                mInCallServiceFixtureX.getCall(ids.mCallId).getCreationTimeMillis());
+        assertEquals(TEST_CREATE_TIME,
+                mInCallServiceFixtureY.getCall(ids.mCallId).getCreationTimeMillis());
+
         verifyNoBlockChecks();
     }
 
@@ -95,6 +106,8 @@
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
@@ -219,6 +232,8 @@
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
@@ -231,6 +246,8 @@
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
@@ -290,6 +307,8 @@
         IdPair ids = outgoingCallPhoneAccountSelected(mPhoneAccountA0.getAccountHandle(),
                 startingNumConnections, startingNumCalls, mConnectionServiceFixtureA);
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED,
                 mInCallServiceFixtureX.getCall(ids.mCallId).getState());
@@ -341,6 +360,8 @@
 
     @LargeTest
     public void testIncomingCallCallerInfoLookupTimesOutIsAllowed() throws Exception {
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CREATE_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CREATE_ELAPSED_TIME);
         Bundle extras = new Bundle();
         extras.putParcelable(
                 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
@@ -377,6 +398,8 @@
         verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
                 .addCall(any(ParcelableCall.class));
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CONNECT_ELAPSED_TIME);
         disconnectCall(mInCallServiceFixtureX.mLatestCallId,
                 mConnectionServiceFixtureA.mLatestConnectionId);
     }
@@ -736,9 +759,15 @@
     }
 
     private void disconnectCall(String callId, String connectionId) throws Exception {
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_DISCONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_DISCONNECT_ELAPSED_TIME);
         mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL);
         assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState());
         assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState());
+        assertEquals(TEST_CREATE_TIME,
+                mInCallServiceFixtureX.getCall(callId).getCreationTimeMillis());
+        assertEquals(TEST_CREATE_TIME,
+                mInCallServiceFixtureY.getCall(callId).getCreationTimeMillis());
     }
 
     /**
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index e815b5c..39f70c8 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -50,7 +50,6 @@
 
 import java.lang.Override;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -410,6 +409,7 @@
         IVideoProvider videoProvider;
         int videoState;
         long connectTimeMillis;
+        long connectElapsedTimeMillis;
         StatusHints statusHints;
         Bundle extras;
     }
@@ -640,6 +640,7 @@
                 c.videoProvider,
                 c.videoState,
                 c.connectTimeMillis,
+                c.connectElapsedTimeMillis,
                 c.statusHints,
                 c.extras);
     }
@@ -660,6 +661,7 @@
                 false, /* ringback requested */
                 false, /* voip audio mode */
                 0, /* Connect Time for conf call on this connection */
+                0, /* Connect Real Time comes from conference call */
                 c.statusHints,
                 c.disconnectCause,
                 c.conferenceableConnectionIds,
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index ca38eb9..0832e7a 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -64,11 +64,10 @@
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.BluetoothPhoneServiceImpl;
 import com.android.server.telecom.CallAudioManager;
-import com.android.server.telecom.CallAudioRouteStateMachine;
-import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -83,7 +82,6 @@
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
-import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
@@ -108,6 +106,16 @@
     static final int TEST_POLL_INTERVAL = 10;  // milliseconds
     static final int TEST_TIMEOUT = 1000;  // milliseconds
 
+    // Purposely keep the connect time (which is wall clock) and elapsed time (which is time since
+    // boot) different to test that wall clock time operations and elapsed time operations perform
+    // as they individually should.
+    static final long TEST_CREATE_TIME = 100;
+    static final long TEST_CREATE_ELAPSED_TIME = 200;
+    static final long TEST_CONNECT_TIME = 1000;
+    static final long TEST_CONNECT_ELAPSED_TIME = 2000;
+    static final long TEST_DISCONNECT_TIME = 8000;
+    static final long TEST_DISCONNECT_ELAPSED_TIME = 4000;
+
     public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
         @Override
         public HeadsetMediaButton create(Context context, CallsManager callsManager,
@@ -197,6 +205,7 @@
     @Mock AsyncRingtonePlayer mAsyncRingtonePlayer;
     @Mock InterruptionFilterProxy mInterruptionFilterProxy;
     @Mock IncomingCallNotifier mIncomingCallNotifier;
+    @Mock ClockProxy mClockProxy;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -394,7 +403,9 @@
         when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class)))
                 .thenReturn(TEST_TIMEOUT / 5L);
         mIncomingCallNotifier = mock(IncomingCallNotifier.class);
-
+        mClockProxy = mock(ClockProxy.class);
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CREATE_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CREATE_ELAPSED_TIME);
         mTelecomSystem = new TelecomSystem(
                 mComponentContextFixture.getTestDouble(),
                 new MissedCallNotifierImplFactory() {
@@ -427,7 +438,8 @@
                 mAsyncRingtonePlayer,
                 mPhoneNumberUtilsAdapter,
                 mInterruptionFilterProxy,
-                mIncomingCallNotifier);
+                mIncomingCallNotifier,
+                mClockProxy);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
@@ -885,6 +897,8 @@
 
         connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CONNECT_ELAPSED_TIME);
         connectionServiceFixture.sendSetActive(ids.mConnectionId);
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
@@ -922,6 +936,8 @@
                     .answerVideo(eq(ids.mConnectionId), eq(videoState), any());
         }
 
+        when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CONNECT_TIME);
+        when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CONNECT_ELAPSED_TIME);
         connectionServiceFixture.sendSetActive(ids.mConnectionId);
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());