Merge "RootlessGpuDebug: Change Activity to Service" into sc-v2-dev
diff --git a/hostsidetests/gputools/apps/AndroidManifest.xml b/hostsidetests/gputools/apps/AndroidManifest.xml
index 89ecaf8..3c8de1f 100755
--- a/hostsidetests/gputools/apps/AndroidManifest.xml
+++ b/hostsidetests/gputools/apps/AndroidManifest.xml
@@ -19,13 +19,13 @@
      package="android.rootlessgpudebug.app">
 
     <application android:extractNativeLibs="true">
-        <activity android:name=".RootlessGpuDebugDeviceActivity"
+        <service android:name=".RootlessGpuDebugService"
+             android:process=":target_api_service"
              android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.service.action.TARGET_API_SERVICE"/>
             </intent-filter>
-        </activity>
+        </service>
     </application>
 
 </manifest>
diff --git a/hostsidetests/gputools/apps/inject/AndroidManifest.xml b/hostsidetests/gputools/apps/inject/AndroidManifest.xml
index 63bd9c1..743c2be 100644
--- a/hostsidetests/gputools/apps/inject/AndroidManifest.xml
+++ b/hostsidetests/gputools/apps/inject/AndroidManifest.xml
@@ -21,13 +21,12 @@
     <application android:extractNativeLibs="true">
         <meta-data android:name="com.android.graphics.injectLayers.enable"
              android:value="true"/>
-        <activity android:name=".RootlessGpuDebugDeviceActivity"
-             android:exported="true">
+        <service android:name=".RootlessGpuDebugService"
+                android:process=":target_api_service"
+                android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.service.action.TARGET_API_SERVICE"/>
             </intent-filter>
-        </activity>
+        </service>
     </application>
-
 </manifest>
diff --git a/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
index f62a583..44317c5 100644
--- a/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
+++ b/hostsidetests/gputools/apps/jni/android_gputools_cts_RootlessGpuDebug.cpp
@@ -17,9 +17,6 @@
 
 #define LOG_TAG "RootlessGpuDebug"
 
-#include <string>
-#include <vector>
-
 #include <EGL/egl.h>
 #include <GLES3/gl3.h>
 #include <android/log.h>
@@ -27,6 +24,10 @@
 #include <jni.h>
 #include <vulkan/vulkan.h>
 
+#include <sstream>
+#include <string>
+#include <vector>
+
 #define ALOGI(msg, ...) \
     __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
 #define ALOGE(msg, ...) \
@@ -39,7 +40,7 @@
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
 std::string initVulkan() {
-    std::string result = "";
+    std::stringstream result;
 
     {
       uint32_t count = 0;
@@ -78,13 +79,13 @@
     };
     VkInstance instance;
     VkResult vkResult = vkCreateInstance(&instance_info, nullptr, &instance);
-    if (vkResult == VK_ERROR_INITIALIZATION_FAILED) {
-        result = "vkCreateInstance failed, meaning layers could not be chained.";
+    if (vkResult == VK_SUCCESS) {
+        result << "vkCreateInstance succeeded.";
     } else {
-        result = "vkCreateInstance succeeded.";
+        result << "vkCreateInstance failed with VkResult: " << vkResult;
     }
 
-    return result;
+    return result.str();
 }
 
 std::string initGLES() {
@@ -163,8 +164,7 @@
 }  // anonymous namespace
 
 int register_android_gputools_cts_RootlessGpuDebug(JNIEnv* env) {
-    jclass clazz = env->FindClass(
-        "android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity");
+    jclass clazz = env->FindClass("android/rootlessgpudebug/app/RootlessGpuDebugService");
     return env->RegisterNatives(clazz, gMethods,
                                 sizeof(gMethods) / sizeof(JNINativeMethod));
 }
diff --git a/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
deleted file mode 100644
index a29a246..0000000
--- a/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugDeviceActivity.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 android.rootlessgpudebug.app;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.Override;
-
-public class RootlessGpuDebugDeviceActivity extends Activity {
-
-    static {
-        System.loadLibrary("ctsgputools_jni");
-    }
-
-    private static final String TAG = RootlessGpuDebugDeviceActivity.class.getSimpleName();
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        String result = nativeInitVulkan();
-        Log.i(TAG, result);
-
-        result = nativeInitGLES();
-        Log.i(TAG, result);
-
-        Log.i(TAG, "RootlessGpuDebug activity complete");
-    }
-
-    private static native String nativeInitVulkan();
-    private static native String nativeInitGLES();
-
-}
-
diff --git a/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugService.java b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugService.java
new file mode 100644
index 0000000..47b1ad6
--- /dev/null
+++ b/hostsidetests/gputools/apps/src/android/rootlessgpudebug/app/RootlessGpuDebugService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 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 android.rootlessgpudebug.app;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.lang.Override;
+
+
+public class RootlessGpuDebugService extends Service {
+
+    private static final String TAG = RootlessGpuDebugService.class.getSimpleName();
+
+    static {
+        System.loadLibrary("ctsgputools_jni");
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // We don't provide binding, so return null
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+
+        // The target API is provided via Intent extras
+        String API = null;
+
+        if (intent == null) {
+            throw new AssertionError("No Intent provided to " + TAG);
+        }
+
+        Bundle bundle = intent.getExtras();
+
+        // Ensure the service was started with extras
+        if (bundle == null) {
+            throw new AssertionError("Failed to get Intent extras for " + TAG);
+        }
+
+        API = bundle.getString("API");
+
+        // Without an API to target, we're done
+        if (API == null) {
+            throw new AssertionError("No API provided for " + TAG);
+        }
+
+        // Only three combinations are expected
+        Boolean supportedApi = API.equals("Vulkan") ||
+                               API.equals("GLES") ||
+                               API.equals("Both");
+        if (!supportedApi) {
+            throw new AssertionError("Unsupported API " + API + " in " + TAG);
+        }
+
+        // For each choice, init the target API
+        if (API.equals("Vulkan") || API.equals("Both")) {
+            String result = nativeInitVulkan();
+            Log.i(TAG, result);
+        }
+
+        if (API.equals("GLES") || API.equals("Both")) {
+            String result = nativeInitGLES();
+            Log.i(TAG, result);
+        }
+
+        // Mark service completion
+        Log.i(TAG, "RootlessGpuDebugService complete");
+
+        // Don't try to restart when this is shut down
+        return START_NOT_STICKY;
+    }
+
+    private static native String nativeInitVulkan();
+    private static native String nativeInitGLES();
+}
+
diff --git a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
index 4ec4ef8..3f4c4c9 100644
--- a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
+++ b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
@@ -33,7 +33,7 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class CtsRootlessGpuDebugHostTest extends BaseHostJUnit4Test {
 
-    public static final String TAG = "RootlessGpuDebugDeviceActivity";
+    public static final String TAG = "RootlessGpuDebugService";
 
     // This test ensures that the Vulkan and GLES loaders can use Settings to load layers
     // from the base directory of debuggable applications.  Is also tests several
@@ -90,8 +90,9 @@
     // Positive combined tests
     // - Ensure we can load Vulkan and GLES layers at the same time, from multiple external apps (testMultipleExternalApps)
 
-    private static final String CLASS = "RootlessGpuDebugDeviceActivity";
-    private static final String ACTIVITY = "android.rootlessgpudebug.app.RootlessGpuDebugDeviceActivity";
+    private static final String API_VULKAN = "Vulkan";
+    private static final String API_GLES = "GLES";
+    private static final String API_BOTH = "Both";
     private static final String VK_LAYER_LIB_PREFIX = "libVkLayer_nullLayer";
     private static final String VK_LAYER_A_LIB = VK_LAYER_LIB_PREFIX + "A.so";
     private static final String VK_LAYER_B_LIB = VK_LAYER_LIB_PREFIX + "B.so";
@@ -257,7 +258,7 @@
                     result.found = true;
                     result.lineNumber = lineNumber;
                 }
-                if (line.contains("RootlessGpuDebug activity complete")) {
+                if (line.contains("RootlessGpuDebugService complete")) {
                     // Once we've got output from the app, we've collected what we need
                     scanComplete= true;
                 }
@@ -311,6 +312,20 @@
     }
 
     /**
+     * Launch our test as a background service, avoiding any platform rendering code
+     */
+    private void launchBackgroundService(String appName, String Api) throws Exception {
+
+        // Allow the app to be launched as a background service
+        getDevice().executeAdbCommand("shell", "cmd", "deviceidle", "tempwhitelist", appName);
+
+        // Start the service and tell it to init Vulkan/GLES/Both
+        getDevice().executeAdbCommand("shell", "am", "startservice", "-a", "android.service.action.TARGET_API_SERVICE",
+                                      "--es", "API", Api, appName);
+    }
+
+
+    /**
      * This is the primary test of the feature. It pushes layers to our debuggable app and ensures they are
      * loaded in the correct order.
      */
@@ -326,7 +341,6 @@
         setupLayer(VK_LAYER_A_LIB, LAYERS_APP);
         setupLayer(VK_LAYER_B_LIB, LAYERS_APP);
 
-
         // Copy them over to our DEBUG app
         getDevice().executeAdbCommand("shell", "cat", "/data/local/tmp/" + VK_LAYER_A_LIB, "|",
                 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()),
@@ -335,10 +349,9 @@
                 "run-as", DEBUG_APP, "--user", Integer.toString(getDevice().getCurrentUser()),
                 "sh", "-c", "\'cat", ">", VK_LAYER_B_LIB, ";", "chmod", "700", VK_LAYER_B_LIB + "\'");
 
-
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_VULKAN);
 
         // Check that both layers were loaded, in the correct order
         String searchStringA = "nullCreateInstance called in " + VK_LAYER_A;
@@ -353,6 +366,7 @@
     }
 
     public void testLayerNotLoadedVulkan(final String APP_NAME) throws Exception {
+
         // Set up a layers to be loaded for RELEASE or INJECT app
         applySetting("enable_gpu_debug_layers", "1");
         applySetting("gpu_debug_app", APP_NAME);
@@ -366,9 +380,9 @@
             "run-as", APP_NAME, "--user", Integer.toString(getDevice().getCurrentUser()),
             "sh", "-c", "\'cat", ">", VK_LAYER_A_LIB, ";", "chmod", "700", VK_LAYER_A_LIB + "\'", "||", "echo", "run-as", "failed");
 
-        // Kick off our RELEASE app
+        // Kick off our app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY);
+        launchBackgroundService(APP_NAME, API_VULKAN);
 
         // Ensure we don't load the layer in base dir
         assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false);
@@ -414,7 +428,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_VULKAN);
 
         // Ensure we don't load the layer in base dir
         assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false);
@@ -442,7 +456,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_VULKAN);
 
         // Ensure we don't load the layer in base dir
         assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false);
@@ -470,7 +484,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_VULKAN);
 
         // Ensure layerA is not loaded
         assertVkLayerLoading(appStartTime, VK_LAYER_A, false);
@@ -492,7 +506,7 @@
 
         // Kick off our RELEASE app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+        launchBackgroundService(RELEASE_APP, API_VULKAN);
 
         // Check that only layerC was loaded
         assertVkLayerEnumeration(appStartTime, VK_LAYER_A, false);
@@ -527,7 +541,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_VULKAN);
 
         // Ensure only layerA is loaded
         assertVkLayerLoading(appStartTime, VK_LAYER_A, true);
@@ -548,9 +562,9 @@
         // Specify the external app that hosts layers
         applySetting("gpu_debug_layer_app", LAYERS_APP);
 
-        // Kick off our DEBUG app
+        // Kick off our app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY);
+        launchBackgroundService(APP_NAME, API_VULKAN);
 
         String[] layerNames = layers.split(":");
         for (String layerName : layerNames) {
@@ -621,7 +635,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_GLES);
 
         // Check that both layers were loaded, in the correct order
         String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A;
@@ -657,7 +671,7 @@
 
         // Kick off our RELEASE app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+        launchBackgroundService(RELEASE_APP, API_GLES);
 
         // Ensure we don't load the layer in base dir
         String searchStringA = GLES_LAYER_A + " loaded";
@@ -686,7 +700,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_GLES);
 
         // Ensure we don't load the layer in base dir
         String searchStringA = GLES_LAYER_A + " loaded";
@@ -715,7 +729,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_GLES);
 
         // Ensure we don't load the layer in base dir
         String searchStringA = GLES_LAYER_A + " loaded";
@@ -744,7 +758,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_GLES);
 
         // Ensure layerA is not loaded
         String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A;
@@ -768,7 +782,7 @@
 
         // Kick off our RELEASE app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", RELEASE_APP + "/" + ACTIVITY);
+        launchBackgroundService(RELEASE_APP, API_GLES);
 
         // Check that both layers were loaded, in the correct order
         String searchStringA = GLES_LAYER_A + "loaded";
@@ -808,7 +822,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_GLES);
 
         // Ensure only layerA is loaded
         String searchStringA = "glesLayer_eglChooseConfig called in " + GLES_LAYER_A;
@@ -829,9 +843,9 @@
         // Specify the external app that hosts layers
         applySetting("gpu_debug_layer_app", GLES_LAYERS_APP);
 
-        // Kick off our DEBUG app
+        // Kick off our app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", APP_NAME + "/" + ACTIVITY);
+        launchBackgroundService(APP_NAME, API_GLES);
 
         // Check that our external layer was loaded
         String searchStringC = "glesLayer_eglChooseConfig called in " + GLES_LAYER_C;
@@ -872,7 +886,7 @@
 
         // Kick off our DEBUG app
         String appStartTime = getTime();
-        getDevice().executeAdbCommand("shell", "am", "start", "-n", DEBUG_APP + "/" + ACTIVITY);
+        launchBackgroundService(DEBUG_APP, API_BOTH);
 
         // Check that external layers were loaded from both apps
         assertVkLayerLoading(appStartTime, VK_LAYER_C, true);