Merge "Bypass telecom sequencing for managed CSs for multiple Calls on the same PA" into main
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 57e64bb..4451a94 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -230,10 +230,12 @@
             @Override
             public void onCommunicationDeviceChanged(AudioDeviceInfo device) {
                 @AudioRoute.AudioRouteType int audioType = device != null
-                        ? DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.get(device.getType())
+                        ? DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.getOrDefault(
+                                device.getType(), TYPE_INVALID)
                         : TYPE_INVALID;
-                Log.i(this, "onCommunicationDeviceChanged: %d", audioType);
-                if (device != null && device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                Log.i(this, "onCommunicationDeviceChanged: device (%s), audioType (%d)",
+                        device, audioType);
+                if (audioType == TYPE_SPEAKER) {
                     if (mCurrentRoute.getType() != TYPE_SPEAKER) {
                         sendMessageWithSessionInfo(SPEAKER_ON);
                     }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 5cc868b..dbd33f7 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -490,6 +490,9 @@
     private final ConnectionServiceFocusManager mConnectionSvrFocusMgr;
     /* Handler tied to thread in which CallManager was initialized. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final HandlerThread mHandlerThread = new HandlerThread("telecomAudioCallbacks",
+            android.os.Process.THREAD_PRIORITY_BACKGROUND);
+    private final Handler mAudioCallbackHandler;
     private final EmergencyCallHelper mEmergencyCallHelper;
     private final RoleManagerAdapter mRoleManagerAdapter;
     private final VoipCallMonitor mVoipCallMonitor;
@@ -657,6 +660,8 @@
         mEmergencyCallDiagnosticLogger = emergencyCallDiagnosticLogger;
         mIncomingCallFilterGraphProvider = incomingCallFilterGraphProvider;
         if (featureFlags.enableCallAudioWatchdog()) {
+            mHandlerThread.start();
+            mAudioCallbackHandler = new Handler(mHandlerThread.getLooper());
             mCallAudioWatchDog = new CallAudioWatchdog(
                     mContext.getSystemService(AudioManager.class),
                     new CallAudioWatchdog.PhoneAccountRegistrarProxy() {
@@ -677,9 +682,10 @@
                                 return -1;
                             }
                         }
-                    }, clockProxy, mHandler,
+                    }, clockProxy, mAudioCallbackHandler,
                     featureFlags.telecomMetricsSupport() ? metricsController : null);
         } else {
+            mAudioCallbackHandler = null;
             mCallAudioWatchDog = null;
         }
 
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index b9841ba..72cb297 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -2945,6 +2945,13 @@
             }
         }
 
+        @Override
+        public void setMetricsTestMode(boolean enabled) {
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.setTestMode(enabled);
+            }
+        }
+
         /**
          * Determines whether there are any ongoing {@link PhoneAccount#CAPABILITY_SELF_MANAGED}
          * calls for a given {@code packageName} and {@code userHandle}.
diff --git a/src/com/android/server/telecom/TelecomShellCommand.java b/src/com/android/server/telecom/TelecomShellCommand.java
index 11ceb26..e840b2a 100644
--- a/src/com/android/server/telecom/TelecomShellCommand.java
+++ b/src/com/android/server/telecom/TelecomShellCommand.java
@@ -83,6 +83,8 @@
     private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
     private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
             "set-test-emergency-phone-account-package-filter";
+    private static final String COMMAND_SET_METRICS_TEST_ENABLED = "set-metrics-test-enabled";
+    private static final String COMMAND_SET_METRICS_TEST_DISABLED = "set-metrics-test-disabled";
     /**
      * Command used to emit a distinct "mark" in the logs.
      */
@@ -184,6 +186,12 @@
                 case COMMAND_LOG_MARK:
                     runLogMark();
                     break;
+                case COMMAND_SET_METRICS_TEST_ENABLED:
+                    mTelecomService.setMetricsTestMode(true);
+                    break;
+                case COMMAND_SET_METRICS_TEST_DISABLED:
+                    mTelecomService.setMetricsTestMode(false);
+                    break;
                 default:
                     return handleDefaultCommands(command);
             }
@@ -262,6 +270,8 @@
                 + "testers to indicate where in the logs various test steps take place.\n"
                 + "telecom is-non-ui-in-call-service-bound <PACKAGE>: queries a particular "
                 + "non-ui-InCallService in InCallController to determine if it is bound \n"
+                + "telecom set-metrics-test-enabled: Enable the metrics test mode.\n"
+                + "telecom set-metrics-test-disabled: Disable the metrics test mode.\n"
         );
     }
     private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
diff --git a/src/com/android/server/telecom/metrics/ApiStats.java b/src/com/android/server/telecom/metrics/ApiStats.java
index 4b23e47..d962276 100644
--- a/src/com/android/server/telecom/metrics/ApiStats.java
+++ b/src/com/android/server/telecom/metrics/ApiStats.java
@@ -169,8 +169,8 @@
     private static final String FILE_NAME = "api_stats";
     private Map<ApiEvent, Integer> mApiStatsMap;
 
-    public ApiStats(@NonNull Context context, @NonNull Looper looper) {
-        super(context, looper);
+    public ApiStats(@NonNull Context context, @NonNull Looper looper, boolean isTestMode) {
+        super(context, looper, isTestMode);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
diff --git a/src/com/android/server/telecom/metrics/AudioRouteStats.java b/src/com/android/server/telecom/metrics/AudioRouteStats.java
index 4611b22..a79fdea 100644
--- a/src/com/android/server/telecom/metrics/AudioRouteStats.java
+++ b/src/com/android/server/telecom/metrics/AudioRouteStats.java
@@ -76,8 +76,8 @@
     private Pair<AudioRouteStatsKey, long[]> mCur;
     private boolean mIsOngoing;
 
-    public AudioRouteStats(@NonNull Context context, @NonNull Looper looper) {
-        super(context, looper);
+    public AudioRouteStats(@NonNull Context context, @NonNull Looper looper, boolean isTestMode) {
+        super(context, looper, isTestMode);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
diff --git a/src/com/android/server/telecom/metrics/CallStats.java b/src/com/android/server/telecom/metrics/CallStats.java
index 7a69445..f518557 100644
--- a/src/com/android/server/telecom/metrics/CallStats.java
+++ b/src/com/android/server/telecom/metrics/CallStats.java
@@ -59,8 +59,8 @@
     private Map<CallStatsKey, CallStatsData> mCallStatsMap;
     private boolean mHasMultipleAudioDevices;
 
-    public CallStats(@NonNull Context context, @NonNull Looper looper) {
-        super(context, looper);
+    public CallStats(@NonNull Context context, @NonNull Looper looper, boolean isTestMode) {
+        super(context, looper, isTestMode);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
diff --git a/src/com/android/server/telecom/metrics/ErrorStats.java b/src/com/android/server/telecom/metrics/ErrorStats.java
index f334710..7f8ddd7 100644
--- a/src/com/android/server/telecom/metrics/ErrorStats.java
+++ b/src/com/android/server/telecom/metrics/ErrorStats.java
@@ -118,8 +118,8 @@
     private static final String FILE_NAME = "error_stats";
     private Map<ErrorEvent, Integer> mErrorStatsMap;
 
-    public ErrorStats(@NonNull Context context, @NonNull Looper looper) {
-        super(context, looper);
+    public ErrorStats(@NonNull Context context, @NonNull Looper looper, boolean isTestMode) {
+        super(context, looper, isTestMode);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
diff --git a/src/com/android/server/telecom/metrics/TelecomMetricsController.java b/src/com/android/server/telecom/metrics/TelecomMetricsController.java
index c642303..23673ca 100644
--- a/src/com/android/server/telecom/metrics/TelecomMetricsController.java
+++ b/src/com/android/server/telecom/metrics/TelecomMetricsController.java
@@ -37,6 +37,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public class TelecomMetricsController implements StatsManager.StatsPullAtomCallback {
 
@@ -45,6 +46,7 @@
     private final Context mContext;
     private final HandlerThread mHandlerThread;
     private final ConcurrentHashMap<Integer, TelecomPulledAtom> mStats = new ConcurrentHashMap<>();
+    private final AtomicBoolean mIsTestMode = new AtomicBoolean(false);
 
     private TelecomMetricsController(@NonNull Context context,
                                      @NonNull HandlerThread handlerThread) {
@@ -76,7 +78,7 @@
         if (stats == null) {
             long token = Binder.clearCallingIdentity();
             try {
-                stats = new ApiStats(mContext, mHandlerThread.getLooper());
+                stats = new ApiStats(mContext, mHandlerThread.getLooper(), isTestMode());
                 registerAtom(stats.getTag(), stats);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -89,7 +91,7 @@
     public AudioRouteStats getAudioRouteStats() {
         AudioRouteStats stats = (AudioRouteStats) mStats.get(CALL_AUDIO_ROUTE_STATS);
         if (stats == null) {
-            stats = new AudioRouteStats(mContext, mHandlerThread.getLooper());
+            stats = new AudioRouteStats(mContext, mHandlerThread.getLooper(), isTestMode());
             registerAtom(stats.getTag(), stats);
         }
         return stats;
@@ -99,7 +101,7 @@
     public CallStats getCallStats() {
         CallStats stats = (CallStats) mStats.get(CALL_STATS);
         if (stats == null) {
-            stats = new CallStats(mContext, mHandlerThread.getLooper());
+            stats = new CallStats(mContext, mHandlerThread.getLooper(), isTestMode());
             registerAtom(stats.getTag(), stats);
         }
         return stats;
@@ -109,7 +111,7 @@
     public ErrorStats getErrorStats() {
         ErrorStats stats = (ErrorStats) mStats.get(TELECOM_ERROR_STATS);
         if (stats == null) {
-            stats = new ErrorStats(mContext, mHandlerThread.getLooper());
+            stats = new ErrorStats(mContext, mHandlerThread.getLooper(), isTestMode());
             registerAtom(stats.getTag(), stats);
         }
         return stats;
@@ -140,14 +142,30 @@
     }
 
     public void destroy() {
+        clearStats();
+        mHandlerThread.quitSafely();
+    }
+
+    public void setTestMode(boolean enabled) {
+        mIsTestMode.set(enabled);
+        clearStats();
+    }
+
+    public boolean isTestMode() {
+        return mIsTestMode.get();
+    }
+
+    private void clearStats() {
         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
         if (statsManager != null) {
-            mStats.forEach((tag, stat) -> statsManager.clearPullAtomCallback(tag));
+            mStats.forEach((tag, stat) -> {
+                statsManager.clearPullAtomCallback(tag);
+                stat.flush();
+            });
         } else {
             Log.w(TAG, "Unable to clear pulled atoms as StatsManager is null");
         }
 
         mStats.clear();
-        mHandlerThread.quitSafely();
     }
 }
diff --git a/src/com/android/server/telecom/metrics/TelecomPulledAtom.java b/src/com/android/server/telecom/metrics/TelecomPulledAtom.java
index 161eaa8..d60fc77 100644
--- a/src/com/android/server/telecom/metrics/TelecomPulledAtom.java
+++ b/src/com/android/server/telecom/metrics/TelecomPulledAtom.java
@@ -45,23 +45,28 @@
     private static final long MIN_PULL_INTERVAL_MILLIS = 23L * 60 * 60 * 1000;
     private static final int EVENT_SAVE = 1;
     protected final Context mContext;
+    protected final boolean mIsTestMode;
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     public PulledAtoms mPulledAtoms;
     protected long mLastPulledTimestamps;
 
-    protected TelecomPulledAtom(@NonNull Context context, @NonNull Looper looper) {
+    protected TelecomPulledAtom(@NonNull Context context, @NonNull Looper looper,
+                                boolean isTestMode) {
         super(looper);
         mContext = context;
+        mIsTestMode = isTestMode;
         mPulledAtoms = loadAtomsFromFile();
         onLoad();
     }
 
     public synchronized int pull(final List<StatsEvent> data) {
-        long cur = System.currentTimeMillis();
-        if (cur - mLastPulledTimestamps < MIN_PULL_INTERVAL_MILLIS) {
-            return StatsManager.PULL_SKIP;
+        if (!mIsTestMode) {
+            long cur = System.currentTimeMillis();
+            if (cur - mLastPulledTimestamps < MIN_PULL_INTERVAL_MILLIS) {
+                return StatsManager.PULL_SKIP;
+            }
+            mLastPulledTimestamps = cur;
         }
-        mLastPulledTimestamps = cur;
         return onPull(data);
     }
 
@@ -76,21 +81,22 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     public abstract void onAggregate();
 
-    public void onFlush() {
+    public void flush() {
         save(0);
     }
 
     protected abstract String getFileName();
 
     private synchronized PulledAtoms loadAtomsFromFile() {
-        try {
-            return
-                    PulledAtoms.parseFrom(
-                            Files.readAllBytes(mContext.getFileStreamPath(getFileName()).toPath()));
-        } catch (NoSuchFileException e) {
-            Log.e(TAG, e, "the atom file not found");
-        } catch (IOException | NullPointerException e) {
-            Log.e(TAG, e, "cannot load/parse the atom file");
+        if (!mIsTestMode) {
+            try {
+                return PulledAtoms.parseFrom(
+                        Files.readAllBytes(mContext.getFileStreamPath(getFileName()).toPath()));
+            } catch (NoSuchFileException e) {
+                Log.e(TAG, e, "the atom file not found");
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, e, "cannot load/parse the atom file");
+            }
         }
         return makeNewPulledAtoms();
     }
@@ -100,14 +106,16 @@
     }
 
     private synchronized void onSave() {
-        try (FileOutputStream stream = mContext.openFileOutput(getFileName(),
-                Context.MODE_PRIVATE)) {
-            Log.d(TAG, "save " + getTag());
-            stream.write(PulledAtoms.toByteArray(mPulledAtoms));
-        } catch (IOException e) {
-            Log.e(TAG, e, "cannot save the atom to file");
-        } catch (UnsupportedOperationException e) {
-            Log.e(TAG, e, "cannot open the file");
+        if (!mIsTestMode) {
+            try (FileOutputStream stream = mContext.openFileOutput(getFileName(),
+                    Context.MODE_PRIVATE)) {
+                Log.d(TAG, "save " + getTag());
+                stream.write(PulledAtoms.toByteArray(mPulledAtoms));
+            } catch (IOException e) {
+                Log.e(TAG, e, "cannot save the atom to file");
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, e, "cannot open the file");
+            }
         }
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 7646c2d..c403c50 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -1282,7 +1282,7 @@
         // Stub intent for call2
         Intent callIntent2 = new Intent();
         Bundle callExtras1 = new Bundle();
-        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        Icon icon = Icon.createWithContentUri("content://12@media/external/images/media/");
         // Load StatusHints extra into TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS to be processed
         // as the call extras. This will be leveraged in ConnectionServiceFixture to set the
         // StatusHints for the given connection.
@@ -1315,7 +1315,7 @@
     @Test
     public void testValidateStatusHintsImage_handleCreateConnectionComplete() throws Exception {
         Bundle extras = new Bundle();
-        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        Icon icon = Icon.createWithContentUri("content://12@media/external/images/media/");
         // Load the bundle with the test extra in order to simulate an app directly invoking the
         // binder on ConnectionServiceWrapper#handleCreateConnectionComplete.
         StatusHints statusHints = new StatusHints(icon);
@@ -1349,7 +1349,7 @@
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
 
         // Modify existing connection with StatusHints image exploit
-        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        Icon icon = Icon.createWithContentUri("content://12@media/external/images/media/");
         StatusHints statusHints = new StatusHints(icon);
         assertNotNull(statusHints.getIcon());
         ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
@@ -1384,7 +1384,7 @@
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
 
         // Modify existing connection with StatusHints image exploit
-        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        Icon icon = Icon.createWithContentUri("content://12@media/external/images/media/");
         StatusHints modifiedStatusHints = new StatusHints(icon);
         assertNotNull(modifiedStatusHints.getIcon());
         ConnectionServiceFixture.ConnectionInfo connectionInfo = mConnectionServiceFixtureA
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 126e4cc..b95dfef 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -200,7 +200,8 @@
     private static final PhoneAccountHandle SELF_MANAGED_2_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.baz/.Self2"), "Self2");
     private static final PhoneAccountHandle WORK_HANDLE = new PhoneAccountHandle(
-            ComponentName.unflattenFromString("com.foo/.Blah"), "work", new UserHandle(10));
+            ComponentName.unflattenFromString("com.foo/.Blah"), "work",
+            new UserHandle(SECONDARY_USER_ID));
     private static final PhoneAccountHandle SELF_MANAGED_W_CUSTOM_HANDLE = new PhoneAccountHandle(
             new ComponentName(TEST_PACKAGE_NAME, "class"), "1", TEST_USER_HANDLE);
     private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1")
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index b6c3743..23e8dab 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -1307,7 +1307,7 @@
         UserManager userManager = mContext.getSystemService(UserManager.class);
 
         List<UserHandle> users = Arrays.asList(new UserHandle(0),
-                new UserHandle(1000));
+                new UserHandle(10));
 
         PhoneAccount pa1 = new PhoneAccount.Builder(
                 new PhoneAccountHandle(new ComponentName(PACKAGE_1, COMPONENT_NAME), "1234",
diff --git a/tests/src/com/android/server/telecom/tests/TelecomMetricsControllerTest.java b/tests/src/com/android/server/telecom/tests/TelecomMetricsControllerTest.java
index 4d494f3..3836a41 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomMetricsControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomMetricsControllerTest.java
@@ -159,6 +159,36 @@
         assertThat(captor.getValue()).isEqualTo(data);
     }
 
+    @Test
+    public void testSetTestMode() {
+        StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        ApiStats apiStats1 = mTelecomMetricsController.getApiStats();
+        AudioRouteStats audioStats1 = mTelecomMetricsController.getAudioRouteStats();
+        CallStats callStats1 = mTelecomMetricsController.getCallStats();
+        ErrorStats errorStats1 = mTelecomMetricsController.getErrorStats();
+        mTelecomMetricsController.setTestMode(true);
+
+        verify(statsManager, times(1)).clearPullAtomCallback(eq(CALL_AUDIO_ROUTE_STATS));
+        verify(statsManager, times(1)).clearPullAtomCallback(eq(CALL_STATS));
+        verify(statsManager, times(1)).clearPullAtomCallback(eq(TELECOM_API_STATS));
+        verify(statsManager, times(1)).clearPullAtomCallback(eq(TELECOM_ERROR_STATS));
+        assertThat(mTelecomMetricsController.getStats()).isEmpty();
+
+        ApiStats apiStats2 = mTelecomMetricsController.getApiStats();
+        AudioRouteStats audioStats2 = mTelecomMetricsController.getAudioRouteStats();
+        CallStats callStats2 = mTelecomMetricsController.getCallStats();
+        ErrorStats errorStats2 = mTelecomMetricsController.getErrorStats();
+
+        assertThat(apiStats1).isNotSameInstanceAs(apiStats2);
+        assertThat(audioStats1).isNotSameInstanceAs(audioStats2);
+        assertThat(callStats1).isNotSameInstanceAs(callStats2);
+        assertThat(errorStats1).isNotSameInstanceAs(errorStats2);
+
+        mTelecomMetricsController.setTestMode(false);
+
+        assertThat(mTelecomMetricsController.getStats()).isEmpty();
+    }
+
     private void setUpStats() {
         mTelecomMetricsController.getStats().put(CALL_AUDIO_ROUTE_STATS,
                 mAudioRouteStats);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java b/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
index d3c7859..4f7569e 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
@@ -145,22 +145,22 @@
     public void testNewPulledAtomsFromFileInvalid() throws Exception {
         mTempFile.delete();
 
-        ApiStats apiStats = new ApiStats(mSpyContext, mLooper);
+        ApiStats apiStats = new ApiStats(mSpyContext, mLooper, false);
 
         assertNotNull(apiStats.mPulledAtoms);
         assertEquals(apiStats.mPulledAtoms.telecomApiStats.length, 0);
 
-        AudioRouteStats audioRouteStats = new AudioRouteStats(mSpyContext, mLooper);
+        AudioRouteStats audioRouteStats = new AudioRouteStats(mSpyContext, mLooper, false);
 
         assertNotNull(audioRouteStats.mPulledAtoms);
         assertEquals(audioRouteStats.mPulledAtoms.callAudioRouteStats.length, 0);
 
-        CallStats callStats = new CallStats(mSpyContext, mLooper);
+        CallStats callStats = new CallStats(mSpyContext, mLooper, false);
 
         assertNotNull(callStats.mPulledAtoms);
         assertEquals(callStats.mPulledAtoms.callStats.length, 0);
 
-        ErrorStats errorStats = new ErrorStats(mSpyContext, mLooper);
+        ErrorStats errorStats = new ErrorStats(mSpyContext, mLooper, false);
 
         assertNotNull(errorStats.mPulledAtoms);
         assertEquals(errorStats.mPulledAtoms.telecomErrorStats.length, 0);
@@ -169,22 +169,22 @@
     @Test
     public void testNewPulledAtomsFromFileValid() throws Exception {
         createTestFileForApiStats(DEFAULT_TIMESTAMPS_MILLIS);
-        ApiStats apiStats = new ApiStats(mSpyContext, mLooper);
+        ApiStats apiStats = new ApiStats(mSpyContext, mLooper, false);
 
         verifyTestDataForApiStats(apiStats.mPulledAtoms, DEFAULT_TIMESTAMPS_MILLIS);
 
         createTestFileForAudioRouteStats(DEFAULT_TIMESTAMPS_MILLIS);
-        AudioRouteStats audioRouteStats = new AudioRouteStats(mSpyContext, mLooper);
+        AudioRouteStats audioRouteStats = new AudioRouteStats(mSpyContext, mLooper, false);
 
         verifyTestDataForAudioRouteStats(audioRouteStats.mPulledAtoms, DEFAULT_TIMESTAMPS_MILLIS);
 
         createTestFileForCallStats(DEFAULT_TIMESTAMPS_MILLIS);
-        CallStats callStats = new CallStats(mSpyContext, mLooper);
+        CallStats callStats = new CallStats(mSpyContext, mLooper, false);
 
         verifyTestDataForCallStats(callStats.mPulledAtoms, DEFAULT_TIMESTAMPS_MILLIS);
 
         createTestFileForErrorStats(DEFAULT_TIMESTAMPS_MILLIS);
-        ErrorStats errorStats = new ErrorStats(mSpyContext, mLooper);
+        ErrorStats errorStats = new ErrorStats(mSpyContext, mLooper, false);
 
         verifyTestDataForErrorStats(errorStats.mPulledAtoms, DEFAULT_TIMESTAMPS_MILLIS);
     }
@@ -192,7 +192,7 @@
     @Test
     public void testPullApiStatsLessThanMinPullIntervalShouldSkip() throws Exception {
         createTestFileForApiStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS / 2);
-        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
 
         int result = apiStats.pull(data);
@@ -205,7 +205,7 @@
     @Test
     public void testPullApiStatsGreaterThanMinPullIntervalShouldNotSkip() throws Exception {
         createTestFileForApiStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS - 1);
-        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
         int sizePulled = apiStats.mPulledAtoms.telecomApiStats.length;
 
@@ -220,7 +220,7 @@
     @Test
     public void testPullAudioRouteStatsLessThanMinPullIntervalShouldSkip() throws Exception {
         createTestFileForAudioRouteStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS / 2);
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
 
         int result = audioRouteStats.pull(data);
@@ -233,7 +233,7 @@
     @Test
     public void testPullAudioRouteStatsGreaterThanMinPullIntervalShouldNotSkip() throws Exception {
         createTestFileForAudioRouteStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS - 1);
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
         int sizePulled = audioRouteStats.mPulledAtoms.callAudioRouteStats.length;
 
@@ -248,7 +248,7 @@
     @Test
     public void testPullCallStatsLessThanMinPullIntervalShouldSkip() throws Exception {
         createTestFileForCallStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS / 2);
-        CallStats callStats = spy(new CallStats(mSpyContext, mLooper));
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
 
         int result = callStats.pull(data);
@@ -261,7 +261,7 @@
     @Test
     public void testPullCallStatsGreaterThanMinPullIntervalShouldNotSkip() throws Exception {
         createTestFileForCallStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS - 1);
-        CallStats callStats = spy(new CallStats(mSpyContext, mLooper));
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
         int sizePulled = callStats.mPulledAtoms.callStats.length;
 
@@ -276,7 +276,7 @@
     @Test
     public void testPullErrorStatsLessThanMinPullIntervalShouldSkip() throws Exception {
         createTestFileForErrorStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS / 2);
-        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
 
         int result = errorStats.pull(data);
@@ -289,7 +289,7 @@
     @Test
     public void testPullErrorStatsGreaterThanMinPullIntervalShouldNotSkip() throws Exception {
         createTestFileForErrorStats(System.currentTimeMillis() - MIN_PULL_INTERVAL_MILLIS - 1);
-        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper, false));
         final List<StatsEvent> data = new ArrayList<>();
         int sizePulled = errorStats.mPulledAtoms.telecomErrorStats.length;
 
@@ -303,7 +303,7 @@
 
     @Test
     public void testApiStatsLogCount() throws Exception {
-        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper, false));
         ApiStats.ApiEvent event = new ApiStats.ApiEvent(VALUE_API_ID, VALUE_UID, VALUE_API_RESULT);
 
         for (int i = 0; i < 10; i++) {
@@ -384,7 +384,7 @@
         };
         final int[] results = {ApiStats.RESULT_UNKNOWN, ApiStats.RESULT_NORMAL,
                 ApiStats.RESULT_EXCEPTION, ApiStats.RESULT_PERMISSION};
-        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper, false));
         Random rand = new Random();
         Map<ApiStats.ApiEvent, Integer> eventMap = new HashMap<>();
 
@@ -408,7 +408,7 @@
 
     @Test
     public void testAudioRouteStatsLog() throws Exception {
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.log(VALUE_AUDIO_ROUTE_TYPE1, VALUE_AUDIO_ROUTE_TYPE2, true, false,
                 VALUE_AUDIO_ROUTE_LATENCY);
@@ -436,7 +436,7 @@
     @Test
     public void testAudioRouteStatsOnEnterThenExit() throws Exception {
         int latency = 500;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -466,7 +466,7 @@
         int delay = 100;
         int latency = 500;
         int duration = 1000;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -502,7 +502,7 @@
     public void testAudioRouteStatsOnRevertToSourceBeyondThreshold() throws Exception {
         int delay = 100;
         int latency = 500;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -540,7 +540,7 @@
         int delay = 100;
         int latency = 500;
         int duration = 1000;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -575,7 +575,7 @@
     @Test
     public void testAudioRouteStatsOnMultipleEnterWithoutExit() throws Exception {
         int latency = 500;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -596,7 +596,7 @@
     @Test
     public void testAudioRouteStatsOnMultipleEnterWithExit() throws Exception {
         int latency = 500;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
         waitForHandlerActionDelayed(audioRouteStats, TEST_TIMEOUT, latency);
@@ -619,7 +619,7 @@
     @Test
     public void testAudioRouteStatsOnRouteToSameDestWithExit() throws Exception {
         int latency = 500;
-        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper));
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, false));
         doReturn(mMockSourceRoute).when(mMockPendingAudioRoute).getDestRoute();
 
         audioRouteStats.onRouteEnter(mMockPendingAudioRoute);
@@ -640,7 +640,7 @@
 
     @Test
     public void testCallStatsLog() throws Exception {
-        CallStats callStats = spy(new CallStats(mSpyContext, mLooper));
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, false));
 
         callStats.log(VALUE_CALL_DIRECTION, false, false, true, VALUE_CALL_ACCOUNT_TYPE,
                 VALUE_UID, VALUE_CALL_DURATION);
@@ -688,7 +688,7 @@
         doReturn(true).when(account).hasCapabilities(eq(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION));
         doReturn(callingPackage).when(call).getCallingPackageIdentity();
         doReturn(handle).when(call).getTargetPhoneAccount();
-        CallStats callStats = spy(new CallStats(mSpyContext, mLooper));
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, false));
 
         callStats.onCallStart(call);
         waitForHandlerAction(callStats, TEST_TIMEOUT);
@@ -726,7 +726,7 @@
         doReturn(true).when(account).hasCapabilities(eq(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION));
         doReturn(callingPackage).when(call).getCallingPackageIdentity();
         doReturn(handle).when(call).getTargetPhoneAccount();
-        CallStats callStats = spy(new CallStats(mSpyContext, mLooper));
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, false));
 
         callStats.onCallStart(call);
         waitForHandlerAction(callStats, TEST_TIMEOUT);
@@ -744,7 +744,7 @@
 
     @Test
     public void testErrorStatsLogCount() throws Exception {
-        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper, false));
         for (int i = 0; i < 10; i++) {
             errorStats.log(VALUE_MODULE_ID, VALUE_ERROR_ID);
             waitForHandlerAction(errorStats, TEST_TIMEOUT);
@@ -760,7 +760,7 @@
 
     @Test
     public void testErrorStatsLogEvent() throws Exception {
-        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper, false));
         int[] modules = {
                 ErrorStats.SUB_UNKNOWN,
                 ErrorStats.SUB_CALL_AUDIO,
@@ -823,6 +823,54 @@
         }
     }
 
+    @Test
+    public void testApiStatsWithTestModeOn() throws Exception {
+        final List<StatsEvent> data = new ArrayList<>();
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper, true));
+        apiStats.pull(data);
+        apiStats.flush();
+
+        verify(mSpyContext, never()).getFileStreamPath(anyString());
+        verify(apiStats, times(1)).onPull(any());
+        verify(mSpyContext, never()).openFileOutput(anyString(), anyInt());
+    }
+
+    @Test
+    public void testAudioRouteStatsWithTestModeOn() throws Exception {
+        final List<StatsEvent> data = new ArrayList<>();
+        AudioRouteStats audioRouteStats = spy(new AudioRouteStats(mSpyContext, mLooper, true));
+        audioRouteStats.pull(data);
+        audioRouteStats.flush();
+
+        verify(mSpyContext, never()).getFileStreamPath(anyString());
+        verify(audioRouteStats, times(1)).onPull(any());
+        verify(mSpyContext, never()).openFileOutput(anyString(), anyInt());
+    }
+
+    @Test
+    public void testCallStatsWithTestModeOn() throws Exception {
+        final List<StatsEvent> data = new ArrayList<>();
+        CallStats callStats = spy(new CallStats(mSpyContext, mLooper, true));
+        callStats.pull(data);
+        callStats.flush();
+
+        verify(mSpyContext, never()).getFileStreamPath(anyString());
+        verify(callStats, times(1)).onPull(any());
+        verify(mSpyContext, never()).openFileOutput(anyString(), anyInt());
+    }
+
+    @Test
+    public void testErrorStatsWithTestModeOn() throws Exception {
+        final List<StatsEvent> data = new ArrayList<>();
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper, true));
+        errorStats.pull(data);
+        errorStats.flush();
+
+        verify(mSpyContext, never()).getFileStreamPath(anyString());
+        verify(errorStats, times(1)).onPull(any());
+        verify(mSpyContext, never()).openFileOutput(anyString(), anyInt());
+    }
+
     private void createTestFileForApiStats(long timestamps) throws IOException {
         PulledAtomsClass.PulledAtoms atom = new PulledAtomsClass.PulledAtoms();
         atom.telecomApiStats =
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 7d01a44..d002739 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -1096,9 +1096,10 @@
     @Test
     public void testRegisterPhoneAccountImageIconCrossUser() throws RemoteException {
         String packageNameToUse = "com.android.officialpackage";
+        String callingUserId = String.valueOf(Binder.getCallingUserHandle().getIdentifier());
         PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
                 packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
-        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        Icon icon = Icon.createWithContentUri("content://12@media/external/images/media/");
         PhoneAccount phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
         doReturn(PackageManager.PERMISSION_GRANTED)
                 .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
@@ -1108,19 +1109,21 @@
 
         icon = Icon.createWithContentUri(
                 new Uri.Builder().scheme("content")
-                        .encodedAuthority("10%40media")
+                        .encodedAuthority("12%40media")
                         .path("external/images/media/${mediaId.text}".trim())
                         .build());
         phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
         // This should fail; security exception will be thrown
         registerPhoneAccountTestHelper(phoneAccount, false);
 
-        icon = Icon.createWithContentUri( Uri.parse("content://10%40play.ground"));
+        icon = Icon.createWithContentUri( Uri.parse("content://12%40play.ground"));
         phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
         // This should fail; security exception will be thrown
         registerPhoneAccountTestHelper(phoneAccount, false);
 
-        icon = Icon.createWithContentUri("content://0@media/external/images/media/");
+        // Generate a URI referencing the calling/current user ID:
+        String currentUserUri = "content://" + callingUserId + "@media/external/images/media/";
+        icon = Icon.createWithContentUri(currentUserUri);
         phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
         // This should succeed.
         registerPhoneAccountTestHelper(phoneAccount, true);