Android Security CTS/STS hostside Oom Catcher fixes.

There's a NullPointerException due to ThreadLocal variables that got
missed, meaning every branch on or above oc-dev improperly failed tests
when oom was detected.

* Move the ThreadLocal uses to the delegating thread.
* Change the behavior of setting a test to "high-memory"
* Clean up and log device memory caching
* Fix a problem where hard reset was detected when Oom detected.
    - only was a problem when an exception occurred

Bug: 127834624
Bug: 127837702
Bug: 111403569
Test: run "oom(){ oom $1$1;};oom oom" during a test and ensure oom catcher
catches oom cleanly without hard reset detected.
Test: reboot deviced during a test and ensure hard reset is detected.

Change-Id: I87bca9e0ab5563a2b8b9bf90cfb68ff41e9ab8cf
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
index cd39c56..86f930b 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
@@ -79,14 +79,23 @@
      * Utility to get the device memory total by reading /proc/meminfo and returning MemTotal
      */
     private static long getMemTotal(ITestDevice device) throws DeviceNotAvailableException {
-        String memInfo = device.executeShellCommand("cat /proc/meminfo");
-        Pattern pattern = Pattern.compile("MemTotal:\\s*(.*?)\\s*[kK][bB]");
-        Matcher matcher = pattern.matcher(memInfo);
-        if (matcher.find()) {
-            return Long.parseLong(matcher.group(1));
-        } else {
-            throw new RuntimeException("Could not get device memory total");
+        // cache device TotalMem to avoid an adb shell for every test.
+        String serial = device.getSerialNumber();
+        Long totalMemory = totalMemories.get(serial);
+        if (totalMemory == null) {
+            String memInfo = device.executeShellCommand("cat /proc/meminfo");
+            Pattern pattern = Pattern.compile("MemTotal:\\s*(.*?)\\s*[kK][bB]");
+            Matcher matcher = pattern.matcher(memInfo);
+            if (matcher.find()) {
+                totalMemory = Long.parseLong(matcher.group(1));
+            } else {
+                throw new RuntimeException("Could not get device memory total.");
+            }
+            Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG,
+                    "Device " + serial + " has " + totalMemory + "KB total memory.");
+            totalMemories.put(serial, totalMemory);
         }
+        return totalMemory;
     }
 
     /**
@@ -94,24 +103,14 @@
      * Match this call to SecurityTestCase.setup().
      */
     public synchronized void start() throws Exception {
-        // cache device TotalMem to avoid and adb shell for every test.
-        Long totalMemory = totalMemories.get(getDevice().getSerialNumber());
-        if (totalMemory == null) {
-            totalMemory = getMemTotal(getDevice());
-            totalMemories.put(getDevice().getSerialNumber(), totalMemory);
-        }
+        long totalMemory = getMemTotal(getDevice());
         isLowMemoryDevice = totalMemory < LOW_MEMORY_DEVICE_THRESHOLD_KB;
 
         // reset test oom behavior
-        // Low memory devices should skip (pass) tests when OOMing and log so that the
-        // high-memory-test flag can be added. Normal devices should fail tests that OOM so that
-        // they'll be ran again with --retry. If the test OOMs because previous tests used the
-        // memory, it will likely pass on a second try.
-        if (isLowMemoryDevice) {
-            oomBehavior = OomBehavior.PASS_AND_LOG;
-        } else {
-            oomBehavior = OomBehavior.FAIL_AND_LOG;
-        }
+        // Devices should fail tests that OOM so that they'll be ran again with --retry.
+        // If the test OOMs because previous tests used the memory, it will likely pass
+        // on a second try.
+        oomBehavior = OomBehavior.FAIL_AND_LOG;
         oomDetected = false;
 
         // Cache OOM detection in separate persistent threads for each device.
@@ -207,12 +206,11 @@
                     Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG,
                             "lowmemorykiller detected; rebooting device.");
                     synchronized (HostsideOomCatcher.this) { // synchronized for oomDetected
-                        oomDetected = true;
+                        oomDetected = true; // set HostSideOomCatcher var
                     }
                     try {
                         device.nonBlockingReboot();
                         device.waitForDeviceOnline(60 * 2 * 1000); // 2 minutes
-                        context.updateKernelStartTime();
                     } catch (Exception e) {
                         Log.e(LOG_TAG, e.toString());
                     }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index eea1380..ea6d66c 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -96,26 +96,29 @@
         oomCatcher.stop(getDevice().getSerialNumber());
 
         getDevice().waitForDeviceAvailable(120 * 1000);
-        String uptime = getDevice().executeShellCommand("cat /proc/uptime");
-        assertTrue("Phone has had a hard reset",
-            (System.currentTimeMillis()/1000 -
-                Integer.parseInt(uptime.substring(0, uptime.indexOf('.')))
-                    - kernelStartTime < 2));
-        //TODO(badash@): add ability to catch runtime restart
-        getDevice().disableAdbRoot();
 
         if (oomCatcher.isOomDetected()) {
+            // we don't need to check kernel start time if we intentionally rebooted because oom
+            updateKernelStartTime();
             switch (oomCatcher.getOomBehavior()) {
                 case FAIL_AND_LOG:
                     fail("The device ran out of memory.");
-                    return;
+                    break;
                 case PASS_AND_LOG:
                     Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG, "Skipping test.");
-                    return;
+                    break;
                 case FAIL_NO_LOG:
                     fail();
-                    return;
+                    break;
             }
+        } else {
+            String uptime = getDevice().executeShellCommand("cat /proc/uptime");
+            assertTrue("Phone has had a hard reset",
+                (System.currentTimeMillis()/1000 -
+                    Integer.parseInt(uptime.substring(0, uptime.indexOf('.')))
+                        - kernelStartTime < 2));
+            //TODO(badash@): add ability to catch runtime restart
+            getDevice().disableAdbRoot();
         }
     }