Fix testKeyValue_Concurrency flakyness

Bug: 131132243
Test: atest 'LockSettingsStorageTests#testKeyValue_Concurrency'
Change-Id: If14b6fe63ea57837ac3d594c5219b4eb5839002a
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 8af4edd..18453aa 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -27,6 +27,7 @@
 import android.content.pm.UserInfo;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.FileUtils;
+import android.os.SystemClock;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.platform.test.annotations.Presubmit;
@@ -125,7 +126,7 @@
         List<Thread> threads = new ArrayList<>();
         for (int i = 0; i < 100; i++) {
             final int threadId = i;
-            threads.add(new Thread() {
+            threads.add(new Thread("testKeyValue_Concurrency_" + i) {
                 @Override
                 public void run() {
                     synchronized (monitor) {
@@ -134,17 +135,17 @@
                         } catch (InterruptedException e) {
                             return;
                         }
-                        mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
-                        mStorage.readKeyValue("key", "default", 0);
-                        mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
-                        mStorage.readKeyValue("key", "default", 0);
-                        mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
-                        mStorage.readKeyValue("key", "default", 0);
-                        mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
-                        mStorage.readKeyValue("key", "default", 0);
-                        mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
-                        mStorage.readKeyValue("key", "default", 0);
                     }
+                    mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
+                    mStorage.readKeyValue("key", "default", 0);
+                    mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
+                    mStorage.readKeyValue("key", "default", 0);
+                    mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
+                    mStorage.readKeyValue("key", "default", 0);
+                    mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
+                    mStorage.readKeyValue("key", "default", 0);
+                    mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
+                    mStorage.readKeyValue("key", "default", 0);
                 }
             });
             threads.get(i).start();
@@ -153,12 +154,7 @@
         synchronized (monitor) {
             monitor.notifyAll();
         }
-        for (int i = 0; i < threads.size(); i++) {
-            try {
-                threads.get(i).join();
-            } catch (InterruptedException e) {
-            }
-        }
+        joinAll(threads, 10000);
         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
         mStorage.clearCache();
         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
@@ -515,4 +511,29 @@
         }
         return captured[0];
     }
+
+    private static void joinAll(List<Thread> threads, long timeoutMillis) {
+        long deadline = SystemClock.uptimeMillis() + timeoutMillis;
+        for (Thread t : threads) {
+            try {
+                t.join(deadline - SystemClock.uptimeMillis());
+                if (t.isAlive()) {
+                    t.interrupt();
+                    throw new RuntimeException(
+                            "Joining " + t + " timed out. Stack: \n" + getStack(t));
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Interrupted while joining " + t, e);
+            }
+        }
+    }
+
+    private static String getStack(Thread t) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(t.toString()).append('\n');
+        for (StackTraceElement ste : t.getStackTrace()) {
+            sb.append("\tat ").append(ste.toString()).append('\n');
+        }
+        return sb.toString();
+    }
 }