CTS for test-only-flag inheritance

Bug 31382361

Change-Id: I121bf9de24a9d995f89954d944a9f8485c89ef3b
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk
new file mode 100644
index 0000000..a86a98b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAccountCheckTestOnlyOwnerUpdateApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src-owner)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator android-support-test
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml
new file mode 100644
index 0000000..cd186e9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 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.
+ -->
+
+<!-- This package is exactly same as TestOnlyOwner, except for testOnly=false -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.devicepolicy.accountcheck.testonly"
+    android:sharedUserId="com.android.cts.devicepolicy.accountcheck.uid">
+
+    <application android:testOnly="false">
+        <receiver
+            android:name="com.android.cts.devicepolicy.accountcheck.owner.AdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+    <!--
+      Don't need instrumentation. All the three device side apps have the same UID, so we're able
+      to run all tests from the Auth package.
+    -->
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml
new file mode 100644
index 0000000..98e7028
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AccountCheck/TestOnlyOwnerUpdate/res/xml/device_admin.xml
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2016 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.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+</device-admin>
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
index 80142cf..93e8503 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
@@ -16,15 +16,17 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 
+import junit.framework.AssertionFailedError;
+
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class AccountCheckHostSideTest extends BaseDevicePolicyTest {
     private static final String APK_NON_TEST_ONLY = "CtsAccountCheckNonTestOnlyOwnerApp.apk";
     private static final String APK_TEST_ONLY = "CtsAccountCheckTestOnlyOwnerApp.apk";
+    private static final String APK_TEST_ONLY_UPDATE = "CtsAccountCheckTestOnlyOwnerUpdateApp.apk";
     private static final String APK_AUTH = "CtsAccountCheckAuthApp.apk";
 
     private static final String PACKAGE_NON_TEST_ONLY =
@@ -44,12 +46,14 @@
     @Override
     protected void tearDown() throws Exception {
         if (mHasFeature) {
-            runCleanupTestOnlyOwner();
-            runCleanupNonTestOnlyOwner();
+            if (getDevice().getInstalledPackageNames().contains(PACKAGE_AUTH)) {
+                runCleanupTestOnlyOwnerAllowingFailure();
+                runCleanupNonTestOnlyOwnerAllowingFailure();
 
-            // This shouldn't be needed since we're uninstalling the authenticator, but sometimes
-            // the account manager fails to clean up?
-            removeAllAccounts();
+                // This shouldn't be needed since we're uninstalling the authenticator,
+                // but sometimes the account manager fails to clean up?
+                removeAllAccountsAllowingFailure();
+            }
 
             getDevice().uninstallPackage(PACKAGE_AUTH);
             getDevice().uninstallPackage(PACKAGE_TEST_ONLY);
@@ -63,17 +67,38 @@
     }
 
     private void runCleanupTestOnlyOwner() throws Exception {
-        removeAdmin(OWNER_TEST_ONLY, mPrimaryUserId);
+        assertTrue(removeAdmin(OWNER_TEST_ONLY, mPrimaryUserId));
+    }
+
+    private void runCleanupTestOnlyOwnerAllowingFailure() throws Exception {
+        try {
+            runCleanupTestOnlyOwner();
+        } catch (AssertionFailedError ignore) {
+        }
     }
 
     private void runCleanupNonTestOnlyOwner() throws Exception {
         runTest("testCleanUpNonTestOwner");
     }
 
+    private void runCleanupNonTestOnlyOwnerAllowingFailure() throws Exception {
+        try {
+            runCleanupNonTestOnlyOwner();
+        } catch (AssertionFailedError ignore) {
+        }
+    }
+
     private void removeAllAccounts() throws Exception {
         runTest("testRemoveAllAccounts");
     }
 
+    private void removeAllAccountsAllowingFailure() throws Exception {
+        try {
+            removeAllAccounts();
+        } catch (AssertionFailedError ignore) {
+        }
+    }
+
     private void assertTestOnlyInstallable() throws Exception {
         setDeviceOwnerOrFail(OWNER_TEST_ONLY, mPrimaryUserId);
         runCleanupTestOnlyOwner();
@@ -92,18 +117,18 @@
 
     private void assertTestOnlyNotInstallable() throws Exception {
         setDeviceOwnerExpectingFailure(OWNER_TEST_ONLY, mPrimaryUserId);
-        runCleanupTestOnlyOwner();
+        runCleanupTestOnlyOwnerAllowingFailure();
 
         setProfileOwnerExpectingFailure(OWNER_TEST_ONLY, mPrimaryUserId);
-        runCleanupTestOnlyOwner();
+        runCleanupTestOnlyOwnerAllowingFailure();
     }
 
     private void assertNonTestOnlyNotInstallable() throws Exception {
         setDeviceOwnerExpectingFailure(OWNER_NON_TEST_ONLY, mPrimaryUserId);
-        runCleanupNonTestOnlyOwner();
+        runCleanupNonTestOnlyOwnerAllowingFailure();
 
         setProfileOwnerExpectingFailure(OWNER_NON_TEST_ONLY, mPrimaryUserId);
-        runCleanupNonTestOnlyOwner();
+        runCleanupNonTestOnlyOwnerAllowingFailure();
     }
 
     private boolean hasAccounts() throws Exception {
@@ -130,9 +155,9 @@
         installAppAsUser(APK_NON_TEST_ONLY, mPrimaryUserId);
         installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
 
-        runCleanupTestOnlyOwner();
-        runCleanupNonTestOnlyOwner();
-        removeAllAccounts();
+        runCleanupTestOnlyOwnerAllowingFailure();
+        runCleanupNonTestOnlyOwnerAllowingFailure();
+        removeAllAccountsAllowingFailure();
         try {
             runTest("testCheckPreconfiguredAccountFeatures");
 
@@ -209,4 +234,38 @@
             throw th;
         }
     }
+
+    /**
+     * Make sure even if the "test-only" flag changes when an app is updated, we still respect
+     * the original value.
+     */
+    public void testInheritTestOnly() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
+
+            // Set as DO.
+            setDeviceOwnerOrFail(OWNER_TEST_ONLY, mPrimaryUserId);
+
+            // Override with a package that's not test-only.
+            installAppAsUser(APK_TEST_ONLY_UPDATE, mPrimaryUserId);
+
+            // But DPMS keeps the original test-only flag, so it's still removable.
+            runCleanupTestOnlyOwner();
+
+            return;
+        } catch (Throwable e) {
+            // If failed, re-install the APK with test-only=true.
+            try {
+                installAppAsUser(APK_TEST_ONLY, mPrimaryUserId);
+                runCleanupTestOnlyOwner();
+            } catch (Exception inner) {
+                CLog.e("Unable to clean up after a failure: " + e.getMessage());
+            }
+
+            throw e;
+        }
+    }
 }