Merge "cts for service start/launch count"
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index cb5ce8e..768ddee 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1276,7 +1276,7 @@
         </activity>
 
         <activity android:name=".security.BiometricPromptBoundKeysTest"
-            android:label="@string/sec_fingerprint_dialog_bound_key_test"
+            android:label="@string/sec_biometric_prompt_bound_key_test"
             android:configChanges="keyboardHidden|orientation|screenSize" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index a2f12d4..90d1200 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -166,6 +166,7 @@
         complete this test. If available, this test should be run by using fingerprint authentication
         as well as PIN/pattern/password authentication.
     </string>
+
     <string name="sec_fingerprint_bound_key_test">Fingerprint Bound Keys Test</string>
     <string name="sec_fingerprint_bound_key_test_info">
         This test ensures that Keystore cryptographic keys that are bound to fingerprint authentication
@@ -175,7 +176,12 @@
     <string name="sec_fp_dialog_message">Authenticate now with fingerprint</string>
     <string name="sec_fp_auth_failed">Authentication failed</string>
     <string name="sec_start_test">Start Test</string>
-    <string name="sec_fingerprint_dialog_bound_key_test">Fingerprint Bound Keys Test (System Dialog)</string>
+
+    <string name="sec_biometric_prompt_bound_key_test">Biometric Prompt Bound Keys Test</string>
+    <string name="sec_biometric_prompt_bound_key_test_info">
+        This test ensures that Keystore cryptographic keys that are bound to biometric authentication
+        are unusable without an authentication.
+    </string>
 
     <!-- Strings for BluetoothActivity -->
     <string name="bluetooth_test">Bluetooth Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/BiometricPromptBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/BiometricPromptBoundKeysTest.java
index a11eb82..7fece23 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/BiometricPromptBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/BiometricPromptBoundKeysTest.java
@@ -22,6 +22,8 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import com.android.cts.verifier.R;
+
 import java.util.concurrent.Executor;
 
 public class BiometricPromptBoundKeysTest extends FingerprintBoundKeysTest {
@@ -63,7 +65,7 @@
         mCancellationSignal = new CancellationSignal();
         mDialogCallback = new DialogCallback();
         mBiometricPrompt = new BiometricPrompt.Builder(getApplicationContext())
-                .setTitle("Authenticate with fingerprint")
+                .setTitle("Authenticate with biometric")
                 .setNegativeButton("Cancel", mExecutor,
                         (DialogInterface dialogInterface, int which) -> {
                             if (which == DialogInterface.BUTTON_NEGATIVE) {
@@ -76,4 +78,14 @@
                 .CryptoObject(getCipher()),
                 mCancellationSignal, mExecutor, mDialogCallback);
     }
+
+    @Override
+    protected int getTitleRes() {
+        return R.string.sec_biometric_prompt_bound_key_test;
+    }
+
+    @Override
+    protected int getDescriptionRes() {
+        return R.string.sec_biometric_prompt_bound_key_test_info;
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
index 83b84e7..1ae7a12 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
@@ -16,9 +16,6 @@
 
 package com.android.cts.verifier.security;
 
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
 import android.Manifest;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -26,21 +23,23 @@
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.util.Log;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.UserNotAuthenticatedException;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.Toast;
 
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
 import java.io.IOException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -76,12 +75,20 @@
     private FingerprintAuthDialogFragment mFingerprintDialog;
     private Cipher mCipher;
 
+    protected int getTitleRes() {
+        return R.string.sec_fingerprint_bound_key_test;
+    }
+
+    protected int getDescriptionRes() {
+        return R.string.sec_fingerprint_bound_key_test_info;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.sec_screen_lock_keys_main);
         setPassFailButtonClickListeners();
-        setInfoResources(R.string.sec_fingerprint_bound_key_test, R.string.sec_fingerprint_bound_key_test_info, -1);
+        setInfoResources(getTitleRes(), getDescriptionRes(), -1);
         getPassButton().setEnabled(false);
         requestPermissions(new String[]{Manifest.permission.USE_FINGERPRINT},
                 FINGERPRINT_PERMISSION_REQUEST_CODE);
diff --git a/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
index cfc4475..a8b6b3e 100644
--- a/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
+++ b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
@@ -120,21 +120,27 @@
 
     virtual void OnTestPartResult(const testing::TestPartResult &testPartResult) override {
         if (!testPartResult.passed()) {
-            std::ostringstream messageStream;
-            messageStream << testPartResult.file_name() << ":" << testPartResult.line_number()
-                          << "\n" << testPartResult.message();
-            ScopedLocalRef<jstring> jmessage(mEnv, mEnv->NewStringUTF(messageStream.str().c_str()));
+            mCurrentTestError << "\n" << testPartResult.file_name() << ":" << testPartResult.line_number()
+                          << "\n" << testPartResult.message() << "\n";
+        }
+    }
+
+    virtual void OnTestEnd(const testing::TestInfo&) override {
+        const std::string error = mCurrentTestError.str();
+
+        if (!error.empty()) {
+            ScopedLocalRef<jstring> jmessage(mEnv, mEnv->NewStringUTF(error.c_str()));
             ScopedLocalRef<jobject> jthrowable(mEnv, mEnv->NewObject(gAssertionFailure.clazz,
                     gAssertionFailure.ctor, jmessage.get()));
             ScopedLocalRef<jobject> jfailure(mEnv, mEnv->NewObject(gFailure.clazz,
                     gFailure.ctor, mCurrentTestDescription.get(), jthrowable.get()));
             mEnv->CallVoidMethod(mRunNotifier, gRunNotifier.fireTestFailure, jfailure.get());
         }
-    }
 
-    virtual void OnTestEnd(const testing::TestInfo&) override {
         notify(gRunNotifier.fireTestFinished);
         mCurrentTestDescription.reset();
+        mCurrentTestError.str("");
+        mCurrentTestError.clear();
     }
 
     void reportDisabledTests(const std::vector<std::string>& mangledNames) {
@@ -154,6 +160,7 @@
     jobject mRunNotifier;
     jstring mClassName;
     ScopedLocalRef<jobject> mCurrentTestDescription;
+    std::ostringstream mCurrentTestError;
 };
 
 }  // namespace
diff --git a/hostsidetests/incident/apps/graphicsstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/graphicsstatsapp/AndroidManifest.xml
index cfe80be..d3f8870 100644
--- a/hostsidetests/incident/apps/graphicsstatsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/graphicsstatsapp/AndroidManifest.xml
@@ -20,7 +20,9 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name=".DrawFramesActivity" android:label="GraphicsStats Test Activity" />
+        <activity android:name=".DrawFramesActivity"
+                  android:label="GraphicsStats Test Activity"
+                  android:theme="@style/DefaultTheme"/>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/incident/apps/graphicsstatsapp/res/values/themes.xml b/hostsidetests/incident/apps/graphicsstatsapp/res/values/themes.xml
new file mode 100644
index 0000000..b3b1514
--- /dev/null
+++ b/hostsidetests/incident/apps/graphicsstatsapp/res/values/themes.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<resources>
+    <style name="DefaultTheme" parent="@android:style/Theme.Material.NoActionBar.Fullscreen">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:fadingEdge">none</item>
+        <item name="android:windowBackground">@android:color/white</item>
+        <item name="android:windowContentTransitions">false</item>
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index 086f787..c557c9e 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -86,12 +86,22 @@
      */
     @AppModeFull(reason = "Error message is different for instant app (Activity does not exist)")
     public void testNonExportedActivities() throws Exception {
-        // Attempt to launch the non-exported activity in the test app
-        CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
-        mDevice.executeShellCommand(START_NON_EXPORTED_ACTIVITY_COMMAND, outputReceiver);
-        final String output = outputReceiver.getOutput();
+        // Run as unroot
+        boolean wasRoot = mDevice.isAdbRoot();
+        try {
+            mDevice.disableAdbRoot();
+            // Attempt to launch the non-exported activity in the test app
+            CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
+            mDevice.executeShellCommand(START_NON_EXPORTED_ACTIVITY_COMMAND, outputReceiver);
+            final String output = outputReceiver.getOutput();
 
-        assertTrue(output.contains("Permission Denial") && output.contains(" not exported"));
+            assertTrue(output.contains("Permission Denial") && output.contains(" not exported"));
+        } finally {
+            // Restore back to original root state
+            if (wasRoot) {
+                mDevice.enableAdbRoot();
+            }
+        }
     }
 
     public void testIntentFilterHostValidation() throws Exception {
diff --git a/tests/framework/base/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
index b0a780a..6e8f623 100755
--- a/tests/framework/base/activitymanager/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -414,10 +414,13 @@
         <activity android:name=".LaunchTestOnDestroyActivity"
                   android:exported="true"/>
 
-
         <activity android:name=".ReportFullyDrawnActivity"
                   android:exported="true"/>
 
+        <activity android:name=".NoDisplayActivity"
+                  android:exported="true"
+                  android:theme="@android:style/Theme.NoDisplay"/>
+
         <!-- Disable home activities by default or it may disturb other tests by
              showing ResolverActivity when start home activity -->
         <activity-alias android:name=".HomeActivity"
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
index 76b5b79..63c9bd3 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
@@ -65,6 +65,7 @@
     public static final ComponentName MOVE_TASK_TO_BACK_ACTIVITY =
             component("MoveTaskToBackActivity");
     public static final ComponentName NIGHT_MODE_ACTIVITY = component("NightModeActivity");
+    public static final ComponentName NO_DISPLAY_ACTIVITY = component("NoDisplayActivity");
     public static final ComponentName NO_HISTORY_ACTIVITY = component("NoHistoryActivity");
     public static final ComponentName NO_RELAUNCH_ACTIVITY = component("NoRelaunchActivity");
     public static final ComponentName NON_RESIZEABLE_ACTIVITY = component("NonResizeableActivity");
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/NoDisplayActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoDisplayActivity.java
new file mode 100644
index 0000000..1256f28
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoDisplayActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.server.am;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class NoDisplayActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        finish();
+    }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
index 6f2ec8d..eb6e62c 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
@@ -122,10 +122,11 @@
         launchActivity(DEBUGGABLE_APP_ACTIVITY);
 
         executeShellCommand(getStopProfileCmd(DEBUGGABLE_APP_ACTIVITY));
-        // Sleep for 0.1 second (100 milliseconds) so the generation of the profiling
+
+        // Sleep for 0.3 second (300 milliseconds) so the generation of the profiling
         // file is complete.
         try {
-            Thread.sleep(100);
+            Thread.sleep(300);
         } catch (InterruptedException e) {
             //ignored
         }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityMetricsLoggerTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityMetricsLoggerTests.java
index 6ef28c4..0e58956 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityMetricsLoggerTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityMetricsLoggerTests.java
@@ -17,7 +17,10 @@
 package android.server.am;
 
 import static android.os.SystemClock.sleep;
+import static android.server.am.Components.ENTRY_POINT_ALIAS_ACTIVITY;
+import static android.server.am.Components.NO_DISPLAY_ACTIVITY;
 import static android.server.am.Components.REPORT_FULLY_DRAWN_ACTIVITY;
+import static android.server.am.Components.SINGLE_TASK_ACTIVITY;
 import static android.server.am.Components.TEST_ACTIVITY;
 import static android.server.am.Components.TRANSLUCENT_TEST_ACTIVITY;
 
@@ -104,6 +107,7 @@
 
         final long postUptimeMs = SystemClock.uptimeMillis();
         assertMetricsLogs(TEST_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, postUptimeMs);
+        assertTransitionIsStartingWindow(metricsLog);
         final int windowsDrawnDelayMs =
                 (int) metricsLog.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS);
         final String expectedLog =
@@ -129,10 +133,6 @@
         assertThat("reported uptime should be before assertion time", startUptimeSec,
                 lessThanOrEqualTo(postUptimeSec));
         assertNotNull("log should have delay", log.getTaggedData(APP_TRANSITION_DELAY_MS));
-        assertEquals("transition should be started because of starting window",
-                1 /* APP_TRANSITION_STARTING_WINDOW */, log.getSubtype());
-        assertNotNull("log should have starting window delay",
-                log.getTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS));
         assertNotNull("log should have windows drawn delay",
                 log.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS));
         long windowsDrawnDelayMs = (int) log.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS);
@@ -140,6 +140,13 @@
                 windowsDrawnDelayMs,  lessThanOrEqualTo(testElapsedTimeMs));
     }
 
+    private void assertTransitionIsStartingWindow(LogMaker log) {
+        assertEquals("transition should be started because of starting window",
+                1 /* APP_TRANSITION_STARTING_WINDOW */, log.getSubtype());
+        assertNotNull("log should have starting window delay",
+                log.getTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS));
+    }
+
     private void assertEventLogsContainsLaunchTime(List<Event> events, ComponentName componentName,
             int windowsDrawnDelayMs) {
         for (Event event : events) {
@@ -265,6 +272,51 @@
         metricsLog = getMetricsLog(THIRD_ACTIVITY, APP_TRANSITION);
         assertMetricsLogs(THIRD_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs,
                 postUptimeMs);
+        assertTransitionIsStartingWindow(metricsLog);
+    }
+
+    /**
+     * Launch a NoDisplay activity and verify it does not affect subsequent activity launch
+     * metrics. NoDisplay activities do not draw any windows and may be incorrectly identified as a
+     * trampoline activity. See b/80380150 (Long warm launch times reported in dev play console)
+     */
+    @Test
+    public void testNoDisplayActivityLaunch() {
+        getLaunchActivityBuilder()
+                .setUseInstrumentation()
+                .setTargetActivity(NO_DISPLAY_ACTIVITY)
+                .setWaitForLaunched(true)
+                .execute();
+
+        mPreUptimeMs = SystemClock.uptimeMillis();
+        getLaunchActivityBuilder()
+                .setUseInstrumentation()
+                .setTargetActivity(SECOND_ACTIVITY)
+                .setWaitForLaunched(true)
+                .execute();
+        final LogMaker metricsLog = getMetricsLog(SECOND_ACTIVITY, APP_TRANSITION);
+        final long postUptimeMs = SystemClock.uptimeMillis();
+        assertMetricsLogs(SECOND_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, postUptimeMs);
+        assertTransitionIsStartingWindow(metricsLog);
+    }
+
+    /**
+     * Launch an activity with a trampoline activity and verify launch metrics measures the complete
+     * launch sequence from when the trampoline activity is launching to when the target activity
+     * draws on screen.
+     */
+    @Test
+    public void testTrampolineActivityLaunch() {
+        // Launch a trampoline activity that will launch single task activity.
+        getLaunchActivityBuilder()
+                .setUseInstrumentation()
+                .setTargetActivity(ENTRY_POINT_ALIAS_ACTIVITY)
+                .setWaitForLaunched(true)
+                .execute();
+        final LogMaker metricsLog = getMetricsLog(SINGLE_TASK_ACTIVITY, APP_TRANSITION);
+        final long postUptimeMs = SystemClock.uptimeMillis();
+        assertMetricsLogs(SINGLE_TASK_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs,
+                        postUptimeMs);
     }
 
     private LogMaker getMetricsLog(ComponentName componentName, int category) {