Load helper classes from multiple JAR paths

Merged-In: Ief06337f9187501d16c5722379e98b9cfc5a554f

* Setup AUPT test cases with JAR paths to pass to the helper manager
* Move the static variables from the GCA implementation to the base class

Change-Id: I7d55234ece48cfa8a99394681aa694c3f7295fa9
diff --git a/libraries/aupt-lib/src/android/support/test/aupt/AuptTestCase.java b/libraries/aupt-lib/src/android/support/test/aupt/AuptTestCase.java
index 5ec427c..7755fc7 100644
--- a/libraries/aupt-lib/src/android/support/test/aupt/AuptTestCase.java
+++ b/libraries/aupt-lib/src/android/support/test/aupt/AuptTestCase.java
@@ -71,6 +71,7 @@
     private UiWatchers mWatchers;
     private IProcessStatusTracker mProcessStatusTracker;
     private DataCollector mDataCollector;
+    private List<String> mPaths;
 
     // We want to periodically collect dumpheap output if the process grows to large to proactivelly
     // help with catching memory leaks, but don't want to do it too often so it does not disturb the
@@ -805,6 +806,14 @@
         mDataCollector = collector;
     }
 
+    public void setDexedJarPaths(List<String> paths) {
+        mPaths = paths;
+    }
+
+    public List<String> getDexedJarPaths() {
+        return mPaths;
+    }
+
     public String getPackageVersion(String packageName) throws NameNotFoundException {
         if (null == packageName || packageName.isEmpty()) {
               throw new RuntimeException("Package name can't be null or empty");
diff --git a/libraries/aupt-lib/src/android/support/test/aupt/AuptTestRunner.java b/libraries/aupt-lib/src/android/support/test/aupt/AuptTestRunner.java
index 643e681..a689a58 100644
--- a/libraries/aupt-lib/src/android/support/test/aupt/AuptTestRunner.java
+++ b/libraries/aupt-lib/src/android/support/test/aupt/AuptTestRunner.java
@@ -88,6 +88,7 @@
 
     private boolean mTrackJank;
     private GraphicsStatsMonitor mGraphicsStatsMonitor;
+    private List<String> mJars;
 
     /**
      * {@inheritDoc}
@@ -164,7 +165,8 @@
     private void loadDexJars(String jars) {
         // scan provided jar paths, translate relative to absolute paths, and check for existence
         String[] jarsArray = jars.split(":");
-        StringBuilder jarFiles = new StringBuilder();
+        StringBuilder classLoaderPath = new StringBuilder();
+        mJars = new ArrayList<String>();
         for (int i = 0; i < jarsArray.length; i++) {
             String jar = jarsArray[i];
             if (!jar.startsWith("/")) {
@@ -176,9 +178,10 @@
                         + jar);
             }
             if (i != 0) {
-                jarFiles.append(File.pathSeparator);
+                classLoaderPath.append(File.pathSeparator);
             }
-            jarFiles.append(jarFile.getAbsolutePath());
+            classLoaderPath.append(jarFile.getAbsolutePath());
+            mJars.add(jarFile.getAbsolutePath());
         }
         // now load them
         File optDir = new File(getContext().getCacheDir(), DEX_OPT_PATH);
@@ -186,7 +189,7 @@
             throw new RuntimeException(
                     "Failed to create dex optimize directory: " + optDir.getAbsolutePath());
         }
-        mLoader = new BaseDexClassLoader(jarFiles.toString(), optDir, null,
+        mLoader = new BaseDexClassLoader(classLoaderPath.toString(), optDir, null,
                 super.getTargetContext().getClassLoader());
 
     }
@@ -220,7 +223,7 @@
     private void writeProgressMessage(String msg) {
         writeMessage("progress.txt", msg);
     }
-    
+
     private void writeGraphicsMessage(String msg) {
         writeMessage("graphics.txt", msg);
     }
@@ -440,6 +443,7 @@
                 ((AuptTestCase)test).setProcessStatusTracker(mProcessTracker);
                 ((AuptTestCase)test).setMemHealthRecords(mMemHealthRecords);
                 ((AuptTestCase)test).setDataCollector(mDataCollector);
+                ((AuptTestCase)test).setDexedJarPaths(mJars);
             }
         }
 
diff --git a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractGoogleCameraHelper.java b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractGoogleCameraHelper.java
index aa890c0..74e44a8 100644
--- a/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractGoogleCameraHelper.java
+++ b/libraries/base-app-helpers/src/android/platform/test/helpers/AbstractGoogleCameraHelper.java
@@ -20,6 +20,22 @@
 import android.support.test.uiautomator.Direction;
 
 public abstract class AbstractGoogleCameraHelper extends AbstractStandardAppHelper {
+    public static final int HDR_MODE_AUTO = -1;
+    public static final int HDR_MODE_OFF  =  0;
+    public static final int HDR_MODE_ON   =  1;
+
+    public static final int VIDEO_SD_480     = -2;
+    public static final int VIDEO_HD_720     = -1;
+    public static final int VIDEO_HD_1080    =  0;
+    public static final int VIDEO_4K_MODE_ON =  1;
+
+    public static final int VIDEO_30FPS = 0;
+    public static final int VIDEO_60FPS = 1;
+
+    public static final int HFR_MODE_OFF     = 0;
+    public static final int HFR_MODE_120_FPS = 1;
+    public static final int HFR_MODE_240_FPS = 2;
+
 
     public AbstractGoogleCameraHelper(Instrumentation instr) {
         super(instr);
diff --git a/libraries/base-app-helpers/src/android/platform/test/helpers/HelperManager.java b/libraries/base-app-helpers/src/android/platform/test/helpers/HelperManager.java
index 3ac9246..533b0b2 100644
--- a/libraries/base-app-helpers/src/android/platform/test/helpers/HelperManager.java
+++ b/libraries/base-app-helpers/src/android/platform/test/helpers/HelperManager.java
@@ -30,6 +30,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -44,10 +45,9 @@
  * <ol>
  * <li> Static inclusion - if all the code is included in the final APK, the Context can be used to
  * generate a HelperManager and to instantiate implementations.
- * <li> Dexed file inclusion - if this and the helper implementations are bundled into a dexed
- * file, such as a dexed JAR, that gets added after compilation, e.g. how the automated user
- * profile test suites are built, the path can be used to generate a HelperManager and to
- * instantiate implementations.
+ * <li> Dexed file inclusion - if this manager and the helper implementations are bundled into dex
+ * files and loaded from a single class loader, then the files can be used to generate a
+ * HelperManager and to instantiate implementations.
  * </ol>
  * <p>
  * Including and using this strategy will prune the explicit dependency tree for the App Helper
@@ -76,33 +76,36 @@
                         String.format("Cannot pass in null instrumentation."));
             }
             // Instantiation
-            sInstance = new HelperManager(context.getPackageCodePath(), instr);
+            List<String> paths = Arrays.asList(context.getPackageCodePath());
+            sInstance = new HelperManager(paths, instr);
         }
         return sInstance;
     }
 
     /**
-     * Returns an instance of the HelperManager that searches the supplied location for classes to
+     * Returns an instance of the HelperManager that searches the supplied locations for classes to
      * instantiate to helper implementations.
      *
-     * @param path the dexed file where the classes are included
+     * @param paths the dex files where the classes are included
      * @param instr the active instrumentation
      * @throws IllegalArgumentException if the path is not a valid file
      * @return a new instance of the HelperManager class
      */
-    public static HelperManager getInstance(String path, Instrumentation instr) {
+    public static HelperManager getInstance(List<String> paths, Instrumentation instr) {
         if (sInstance == null) {
             // Input checks
-            if (!(new File(path)).exists()) {
-                throw new IllegalArgumentException(
-                        String.format("No file found at path: %s.", path));
+            for (String path : paths) {
+                if (!(new File(path)).exists()) {
+                    throw new IllegalArgumentException(
+                            String.format("No file found at path: %s.", path));
+                }
             }
             if (instr == null) {
                 throw new NullPointerException(
                         String.format("Cannot pass in null instrumentation."));
             }
             // Instantiation
-            sInstance = new HelperManager(path, instr);
+            sInstance = new HelperManager(paths, instr);
         }
         return sInstance;
     }
@@ -110,12 +113,15 @@
     private Instrumentation mInstrumentation;
     private List<String> mClasses;
 
-    private HelperManager(String path, Instrumentation instr) {
+    private HelperManager(List<String> paths, Instrumentation instr) {
         mInstrumentation = instr;
         // Collect all of the available classes
+        mClasses = new ArrayList<String>();
         try {
-            DexFile dex = new DexFile(path);
-            mClasses = Collections.list(dex.entries());
+            for (String path : paths) {
+                DexFile dex = new DexFile(path);
+                mClasses.addAll(Collections.list(dex.entries()));
+            }
         } catch (IOException e) {
             throw new RuntimeException("Failed to retrieve the dex file.");
         }