Merge "Move tests requiring /proc/sys/net access to hostside"
diff --git a/apps/CtsVerifier/res/layout/camera_fov_calibration_photo_capture.xml b/apps/CtsVerifier/res/layout/camera_fov_calibration_photo_capture.xml
index 7f8efa0..c142b15 100644
--- a/apps/CtsVerifier/res/layout/camera_fov_calibration_photo_capture.xml
+++ b/apps/CtsVerifier/res/layout/camera_fov_calibration_photo_capture.xml
@@ -49,7 +49,6 @@
             android:layout_alignParentBottom="true"
             android:layout_alignParentLeft="true"
             android:padding="10sp"
-            android:popupBackground="#ffffff"
             android:textSize="18sp" />
     </RelativeLayout>
 
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 9ec5dd3..bc3146a 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3347,7 +3347,28 @@
     <string name="disallow_outgoing_beam">Disallow outgoing beam</string>
     <string name="disallow_outgoing_beam_action">Switching on android beam</string>
     <string name="disallow_remove_user">Disallow remove user</string>
-    <string name="disallow_remove_user_action">Removing other users (please create a user and attempt to remove it to verify). This test should also be considered as passed if \'remove user\' option cannot be found in Settings</string>
+    <string name="device_owner_disallow_remove_user_info">
+        Please press \'Create uninitialized user\' to create a user that is not set up. Then press the
+        \'Set restriction\' button to set the user restriction. Then press \'Go\' to open \'Settings\',
+        and manually find and open \'Multiple users\' setting. \n\n
+
+        Mark this test as passed if:\n\n
+        - The uninitialized user cannot be removed.\n
+        - \'Remove user\' option is disabled with an info icon on it. Clicking on it triggers a support dialog.\n\n
+
+        Use the Back button to return to this page.
+    </string>
+    <string name="managed_user_disallow_remove_user_info">
+        Please press the \'Set restriction\' button to set the user restriction.
+        Then press \'Go\' to open \'Settings\', and manually find and open \'Multiple users\' setting. \n\n
+
+        Mark this test as passed if one of the following conditions is met:\n\n
+        - \'Remove user\' option is disabled with an info icon on it. Clicking on it triggers a support dialog.\n
+        - \'Remove user\' option cannot be found.\n \n
+
+        Use the Back button to return to this page.
+    </string>
+    <string name="device_owner_disallow_remove_user_create_user">Create uninitialized user</string>
     <string name="disallow_remove_managed_profile">Disallow remove managed profile</string>
     <string name="disallow_remove_managed_profile_action">Removing the work profile. It shouldn\'t be possible neither from the Accounts screen nor the Device Administrators screen (after selecting the Device Administrator that corresponds to the badged version of \"CTS Verifier\")</string>
     <string name="disallow_share_location">Disallow share location</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
index 9cec7fa..a56cd3f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
@@ -46,7 +46,11 @@
 
     private static final String mBuiltInprofiles =
         "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" +
-        "<ProfileList Version=\"1.0.0\">" +
+            "<ProfileList Version=\"1.0.0\">" +
+            "<PeripheralProfile ProfileName=\"AudioBox USB 96\" ProfileDescription=\"PreSonus AudioBox USB 96\" ProductName=\"USB-Audio - AudioBox USB 96\">" +
+                "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+                "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+            "</PeripheralProfile>" +
             "<PeripheralProfile ProfileName=\"Audio Interface\" ProfileDescription=\"Presonus AudioVox 44VSL\" ProductName=\"USB-Audio - AudioBox 44 VSL\">" +
                 "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
                 "<InputDevInfo ChanCounts=\"1,2,4\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
@@ -59,6 +63,18 @@
                 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
                 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
             "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Focusrite 2i4\" ProfileDescription=\"Focusrite Scarlett 2i4\" ProductName=\"USB-Audio - Scarlett 2i4 USB\">" +
+                "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+                "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+            "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Behringer UMC204HD\" ProfileDescription=\"Behringer UMC204HD\" ProductName=\"USB-Audio - UMC204HD 192k\">" +
+                "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"2,4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
+                "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
+            "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Roland Rubix24\" ProfileDescription=\"Roland Rubix24\" ProductName=\"USB-Audio - Rubix24\">" +
+                "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
+                "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
+            "</PeripheralProfile>" +
             "<PeripheralProfile ProfileName=\"Pixel USB-C Dongle + Wired Analog Headset\" ProfileDescription=\"Reference USB Dongle\" ProductName=\"USB-Audio - USB-C to 3.5mm-Headphone Adapte\">" +
                 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"48000\" />" +
                 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"48000\" />" +
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 75d884f..0022518 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -77,6 +77,7 @@
     private static final String MANAGED_USER_TEST_ID = "MANAGED_USER_UI";
     private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
     private static final String DISALLOW_AMBIENT_DISPLAY_ID = "DISALLOW_AMBIENT_DISPLAY";
+    private static final String DISALLOW_REMOVE_USER_TEST_ID = "DISALLOW_REMOVE_USER";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -400,6 +401,22 @@
                             new ButtonInfo(
                                     R.string.device_owner_settings_go,
                                     new Intent(Settings.ACTION_SETTINGS))}));
+
+            // DISALLOW_REMOVE_USER
+            adapter.add(createInteractiveTestItem(this, DISALLOW_REMOVE_USER_TEST_ID,
+                    R.string.disallow_remove_user,
+                    R.string.device_owner_disallow_remove_user_info,
+                    new ButtonInfo[]{
+                            new ButtonInfo(
+                                    R.string.device_owner_disallow_remove_user_create_user,
+                                    createCreateManagedUserWithoutSetupIntent()),
+                            new ButtonInfo(
+                                    R.string.device_owner_user_restriction_set,
+                                    CommandReceiverActivity.createSetUserRestrictionIntent(
+                                            UserManager.DISALLOW_REMOVE_USER, true)),
+                            new ButtonInfo(
+                                    R.string.device_owner_settings_go,
+                                    new Intent(Settings.ACTION_SETTINGS))}));
         }
 
         // Network logging UI
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
index aa20b68..dd2a639 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.database.DataSetObserver;
 import android.os.Bundle;
+import android.os.UserManager;
 import android.provider.Settings;
 
 import com.android.cts.verifier.ArrayTestListAdapter;
@@ -49,6 +50,7 @@
     private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
     private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
     private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
+    private static final String DISALLOW_REMOVE_USER_TEST_ID = "DISALLOW_REMOVE_USER";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -148,6 +150,19 @@
                                         CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
                                         false))}));
 
+        // DISALLOW_REMOVE_USER
+        adapter.add(createInteractiveTestItem(this, DISALLOW_REMOVE_USER_TEST_ID,
+                R.string.disallow_remove_user,
+                R.string.managed_user_disallow_remove_user_info,
+                new ButtonInfo[]{
+                        new ButtonInfo(
+                                R.string.device_owner_user_restriction_set,
+                                CommandReceiverActivity.createSetUserRestrictionIntent(
+                                        UserManager.DISALLOW_REMOVE_USER, true)),
+                        new ButtonInfo(
+                                R.string.device_owner_settings_go,
+                                new Intent(Settings.ACTION_SETTINGS))}));
+
         // Policy Transparency
         final Intent policyTransparencyTestIntent = new Intent(this,
                 PolicyTransparencyTestListActivity.class);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 90f0dc3..f004ace 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -47,7 +47,6 @@
         UserManager.DISALLOW_NETWORK_RESET,
         UserManager.DISALLOW_OUTGOING_BEAM,
         UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
-        UserManager.DISALLOW_REMOVE_USER,
         UserManager.DISALLOW_SHARE_LOCATION,
         UserManager.DISALLOW_UNINSTALL_APPS,
         UserManager.DISALLOW_UNIFIED_PASSWORD,
@@ -77,7 +76,6 @@
             R.string.disallow_network_reset,
             R.string.disallow_outgoing_beam,
             R.string.disallow_remove_managed_profile,
-            R.string.disallow_remove_user,
             R.string.disallow_share_location,
             R.string.disallow_uninstall_apps,
             R.string.disallow_unified_challenge,
@@ -105,7 +103,6 @@
             R.string.disallow_network_reset_action,
             R.string.disallow_outgoing_beam_action,
             R.string.disallow_remove_managed_profile_action,
-            R.string.disallow_remove_user_action,
             R.string.disallow_share_location_action,
             R.string.disallow_uninstall_apps_action,
             R.string.disallow_unified_challenge_action,
@@ -133,7 +130,6 @@
             Settings.ACTION_SETTINGS,
             Settings.ACTION_NFC_SETTINGS,
             Settings.ACTION_SETTINGS,
-            Settings.ACTION_SETTINGS,
             Settings.ACTION_LOCATION_SOURCE_SETTINGS,
             Settings.ACTION_APPLICATION_SETTINGS,
             Settings.ACTION_SECURITY_SETTINGS,
@@ -175,7 +171,6 @@
                     UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                     UserManager.DISALLOW_MODIFY_ACCOUNTS,
                     UserManager.DISALLOW_OUTGOING_BEAM,
-                    UserManager.DISALLOW_REMOVE_USER,
                     UserManager.DISALLOW_SHARE_LOCATION,
                     UserManager.DISALLOW_UNINSTALL_APPS,
                     UserManager.DISALLOW_CONFIG_DATE_TIME,
@@ -237,7 +232,6 @@
         final PackageManager pm = context.getPackageManager();
         switch (restriction) {
             case UserManager.DISALLOW_ADD_USER:
-            case UserManager.DISALLOW_REMOVE_USER:
                 return UserManager.supportsMultipleUsers();
             case UserManager.DISALLOW_ADJUST_VOLUME:
                 return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
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 74a0ba4..83b84e7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
@@ -69,6 +69,8 @@
     private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
     private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
 
+    protected boolean useStrongBox;
+
     private FingerprintManager mFingerprintManager;
     private KeyguardManager mKeyguardManager;
     private FingerprintAuthDialogFragment mFingerprintDialog;
@@ -88,6 +90,7 @@
     @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] state) {
         if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && state[0] == PackageManager.PERMISSION_GRANTED) {
+            useStrongBox = false;
             mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
             mKeyguardManager = (KeyguardManager) getSystemService(KeyguardManager.class);
             Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
@@ -108,18 +111,22 @@
             startTestButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    createKey(false /* hasValidityDuration */);
-                    prepareEncrypt();
-                    if (tryEncrypt()) {
-                        showToast("Test failed. Key accessible without auth.");
-                    } else {
-                        showAuthenticationScreen();
-                    }
+                    startTest();
                 }
             });
         }
     }
 
+    protected void startTest() {
+        createKey(false /* hasValidityDuration */);
+        prepareEncrypt();
+        if (tryEncrypt()) {
+            showToast("Test failed. Key accessible without auth.");
+        } else {
+            showAuthenticationScreen();
+        }
+    }
+
     /**
      * Creates a symmetric key in the Android Key Store which requires auth
      */
@@ -141,6 +148,7 @@
                     .setUserAuthenticationValidityDurationSeconds(
                         hasValidityDuration ? AUTHENTICATION_DURATION_SECONDS : -1)
                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                    .setIsStrongBoxBacked(useStrongBox)
                     .build());
             keyGenerator.generateKey();
             if (DEBUG) {
@@ -176,7 +184,7 @@
         return mCipher;
     }
 
-    protected boolean doValidityDurationTest() {
+    protected boolean doValidityDurationTest(boolean useStrongBox) {
         mCipher = null;
         createKey(true /* hasValidityDuration */);
         if (prepareEncrypt()) {
@@ -251,6 +259,7 @@
         private FingerprintManager mFingerprintManager;
         private FingerprintManagerCallback mFingerprintManagerCallback;
         private boolean mSelfCancelled;
+        private boolean hasStrongBox;
 
         class FingerprintManagerCallback extends FingerprintManager.AuthenticationCallback {
             @Override
@@ -281,15 +290,22 @@
                 if (DEBUG) {
                     Log.i(TAG,"onAuthenticationSucceeded");
                 }
-                if (mActivity.tryEncrypt() && mActivity.doValidityDurationTest()) {
+                hasStrongBox = getContext().getPackageManager()
+                                    .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
+                if (mActivity.tryEncrypt() && mActivity.doValidityDurationTest(false)) {
                     try {
                         Thread.sleep(3000);
                     } catch (Exception e) {
                         throw new RuntimeException("Failed to sleep", e);
                     }
-                    if (!mActivity.doValidityDurationTest()) {
-                        showToast("Test passed.");
-                        mActivity.getPassButton().setEnabled(true);
+                    if (!mActivity.doValidityDurationTest(false)) {
+                        showToast(String.format("Test passed. useStrongBox: %b",
+                                                mActivity.useStrongBox));
+                        if (mActivity.useStrongBox || !hasStrongBox) {
+                            mActivity.getPassButton().setEnabled(true);
+                        } else {
+                            showToast("Rerunning with StrongBox");
+                        }
                         FingerprintAuthDialogFragment.this.dismiss();
                     } else {
                         showToast("Test failed. Key accessible after validity time limit.");
@@ -304,6 +320,11 @@
         public void onDismiss(DialogInterface dialog) {
             mCancellationSignal.cancel();
             mSelfCancelled = true;
+            // Start the test again, but with StrongBox if supported
+            if (!mActivity.useStrongBox && hasStrongBox) {
+                mActivity.useStrongBox = true;
+                mActivity.startTest();
+            }
         }
 
         private void setActivity(FingerprintBoundKeysTest activity) {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 0d09a2d..41f4006 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -404,14 +404,14 @@
         assumeTrue("Skipping test: no rotation support", supportsRotation());
 
         LogSeparator logSeparator = separateLogs();
-        launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
+        launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
 
         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
                 1 /* start */, 1 /* resume */, 0 /* pause */, 0 /* stop */, 0 /* destroy */,
                 0 /* config */);
 
-        launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
+        launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
         mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
 
         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
@@ -421,7 +421,7 @@
                 1 /* start */, 1 /* resume */, 0 /* pause */, 0 /* stop */, 0 /* destroy */,
                 0 /* config */);
 
-        launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
+        launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
 
         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 2 /* create */,
diff --git a/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
index 98e3ba7..83ceabd 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
@@ -93,10 +93,12 @@
                 EXTRA_LAUNCH_ANOTHER_ACTIVITY, getActivityName(TEST_ACTIVITY));
         executeShellCommand(startActivityOnTop);
         mAmWmState.assertActivityDisplayed(TEST_ACTIVITY);
+        final LogSeparator logSeparator = separateLogs();
 
         try (final ScreenDensitySession screenDensitySession = new ScreenDensitySession()) {
             screenDensitySession.setUnsupportedDensity();
 
+            assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
             pressBackButton();
 
             mAmWmState.assertActivityDisplayed(SMALLEST_WIDTH_ACTIVITY);
diff --git a/tests/jdwp/TEST_MAPPING b/tests/jdwp/TEST_MAPPING
new file mode 100644
index 0000000..0c53164
--- /dev/null
+++ b/tests/jdwp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsJdwpTestCases"
+    }
+  ]
+}
diff --git a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/QtaguidPermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/QtaguidPermissionTest.java
deleted file mode 100644
index 60d2a2a..0000000
--- a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/QtaguidPermissionTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package android.net.cts.legacy.api22.permission;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.TrafficStats;
-import android.support.test.filters.MediumTest;
-import android.test.AndroidTestCase;
-
-import java.io.File;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class QtaguidPermissionTest extends AndroidTestCase {
-
-    private static final String QTAGUID_STATS_FILE = "/proc/net/xt_qtaguid/stats";
-
-    @MediumTest
-    public void testDevQtaguidSane() throws Exception {
-        File f = new File("/dev/xt_qtaguid");
-        assertTrue(f.canRead());
-        assertFalse(f.canWrite());
-        assertFalse(f.canExecute());
-    }
-
-    public void testAccessPrivateTrafficStats() throws IOException {
-
-        final int ownAppUid = getContext().getApplicationInfo().uid;
-        try {
-            BufferedReader qtaguidReader = new BufferedReader(new FileReader(QTAGUID_STATS_FILE));
-            String line;
-            // Skip the header line;
-            qtaguidReader.readLine();
-            while ((line = qtaguidReader.readLine()) != null) {
-                String tokens[] = line.split(" ");
-                // Go through all the entries we find the qtaguid stats and fail if we find a stats
-                // with different uid.
-                if (tokens.length > 3 && !tokens[3].equals(String.valueOf(ownAppUid))) {
-                    fail("Other apps detailed traffic stats leaked, self uid: "
-                         + String.valueOf(ownAppUid) + " find uid: " + tokens[3]);
-                }
-            }
-            qtaguidReader.close();
-        } catch (FileNotFoundException e) {
-            fail("Was not able to access qtaguid/stats: " + e);
-        }
-    }
-
-    private void accessOwnTrafficStats(long expectedTxBytes) throws IOException {
-
-        final int ownAppUid = getContext().getApplicationInfo().uid;
-
-        long totalTxBytes = 0;
-        try {
-            BufferedReader qtaguidReader = new BufferedReader(new FileReader(QTAGUID_STATS_FILE));
-            String line;
-            while ((line = qtaguidReader.readLine()) != null) {
-                String tokens[] = line.split(" ");
-                if (tokens.length > 3 && tokens[3].equals(String.valueOf(ownAppUid))) {
-                    // Check the total stats of this uid is larger then 1MB
-                    if (tokens[2].equals("0x0")) {
-                        totalTxBytes += Integer.parseInt(tokens[7]);
-                    }
-                }
-            }
-            qtaguidReader.close();
-        } catch (FileNotFoundException e) {
-            fail("Was not able to access qtaguid/stats: " + e);
-        }
-        assertTrue(totalTxBytes + " expected to be greater than or equal to"
-            + expectedTxBytes + "bytes", totalTxBytes >= expectedTxBytes);
-    }
-
-    public void testAccessOwnQtaguidTrafficStats() throws IOException {
-
-        // Transfer 1MB of data across an explicitly localhost socket.
-        final int byteCount = 1024;
-        final int packetCount = 1024;
-
-        final ServerSocket server = new ServerSocket(0);
-        new Thread("CreatePrivateDataTest.createTrafficStatsWithTags") {
-            @Override
-            public void run() {
-                try {
-                    Socket socket = new Socket("localhost", server.getLocalPort());
-                    // Make sure that each write()+flush() turns into a packet:
-                    // disable Nagle.
-                    socket.setTcpNoDelay(true);
-                    OutputStream out = socket.getOutputStream();
-                    byte[] buf = new byte[byteCount];
-                    for (int i = 0; i < packetCount; i++) {
-                        TrafficStats.setThreadStatsTag(i % 10);
-                        TrafficStats.tagSocket(socket);
-                        out.write(buf);
-                        out.flush();
-                    }
-                    out.close();
-                    socket.close();
-                } catch (IOException e) {
-                  assertTrue("io exception" + e, false);
-                }
-            }
-        }.start();
-
-        try {
-            Socket socket = server.accept();
-            InputStream in = socket.getInputStream();
-            byte[] buf = new byte[byteCount];
-            int read = 0;
-            while (read < byteCount * packetCount) {
-                int n = in.read(buf);
-                assertTrue("Unexpected EOF", n > 0);
-                read += n;
-            }
-        } finally {
-            server.close();
-        }
-
-        accessOwnTrafficStats(byteCount * packetCount);
-    }
-}
diff --git a/tests/openglperf2/AndroidTest.xml b/tests/openglperf2/AndroidTest.xml
index 4ac0455..c0612be 100644
--- a/tests/openglperf2/AndroidTest.xml
+++ b/tests/openglperf2/AndroidTest.xml
@@ -23,7 +23,5 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.opengl2.cts" />
         <option name="runtime-hint" value="4m" />
-        <!-- test-timeout unit is ms, value = 100 min -->
-        <option name="test-timeout" value="6000000" />
     </test>
 </configuration>
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
index 30b96e7..ce313b8 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -22,7 +22,9 @@
 
     srcs: [
         "android_binder_cts_ibinder.cpp",
+        "android_binder_cts_ibinder_jni.cpp",
         "android_binder_cts_parcel.cpp",
+        "android_binder_cts_status.cpp",
         "utilities.cpp",
     ],
 
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder_jni.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder_jni.cpp
new file mode 100644
index 0000000..228dac3
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_ibinder_jni.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "Cts-NdkBinderTest"
+
+#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_jni.h>
+#include <gtest/gtest.h>
+#include <nativetesthelper_jni/utils.h>
+
+#include "utilities.h"
+
+void* NothingClass_onCreate(void* args) { return args; }
+void NothingClass_onDestroy(void* /*userData*/) {}
+binder_status_t NothingClass_onTransact(AIBinder*, transaction_code_t,
+                                        const AParcel*, AParcel*) {
+  return STATUS_UNKNOWN_ERROR;
+}
+
+static AIBinder_Class* kNothingClass =
+    AIBinder_Class_define("nothing", NothingClass_onCreate,
+                          NothingClass_onDestroy, NothingClass_onTransact);
+
+class NdkBinderTest_AIBinder_Jni : public NdkBinderTest {};
+
+TEST_F(NdkBinderTest_AIBinder_Jni, ConvertJni) {
+  JNIEnv* env = GetEnv();
+  ASSERT_NE(nullptr, env);
+
+  AIBinder* binder = AIBinder_new(kNothingClass, nullptr);
+  EXPECT_NE(nullptr, binder);
+
+  jobject object = AIBinder_toJavaBinder(env, binder);
+  EXPECT_NE(nullptr, object);
+
+  AIBinder* fromJavaBinder = AIBinder_fromJavaBinder(env, object);
+  EXPECT_EQ(binder, fromJavaBinder);
+
+  AIBinder_decStrong(binder);
+  AIBinder_decStrong(fromJavaBinder);
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp
index 0b72824..1f779ac 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_parcel.cpp
@@ -26,13 +26,45 @@
 
 class NdkBinderTest_AParcel : public NdkBinderTest {};
 
+template <typename T, typename Enable = void>
+struct WriteFrom {
+  using type = const T;
+};
+template <>
+struct WriteFrom<AStatus*> {
+  // not 'const T' = 'AStatus* const' where T = AStatus*.
+  using type = const AStatus*;
+};
+
+template <typename T>
+bool NdkBinderSenseOfEquality(T a, T b) {
+  return a == b;
+}
+template <>
+bool NdkBinderSenseOfEquality<const AStatus*>(const AStatus* a,
+                                              const AStatus* b) {
+  if (a == b) return true;
+
+  return AStatus_isOk(a) == AStatus_isOk(b) &&
+         AStatus_getExceptionCode(a) == AStatus_getExceptionCode(b) &&
+         AStatus_getServiceSpecificError(a) ==
+             AStatus_getServiceSpecificError(b) &&
+         AStatus_getStatus(a) == AStatus_getStatus(b) &&
+         std::string(AStatus_getMessage(a)) == AStatus_getMessage(b);
+}
+template <>
+bool NdkBinderSenseOfEquality<AStatus*>(AStatus* a, AStatus* b) {
+  return NdkBinderSenseOfEquality<const AStatus*>(a, b);
+}
+
 // These reads and writes an array of possible values all of the same type.
-template <typename T, binder_status_t (*write)(AParcel*, const T),
+template <typename T,
+          binder_status_t (*write)(AParcel*, typename WriteFrom<T>::type),
           binder_status_t (*read)(const AParcel*, T*)>
 void ExpectInOut(std::vector<T> in) {
   AIBinder* binder = SampleData::newBinder(
       [](transaction_code_t, const AParcel* in, AParcel* out) {
-        T readTarget;
+        T readTarget = {};
         EXPECT_OK(read(in, &readTarget));
         EXPECT_OK(write(out, readTarget));
         return STATUS_OK;
@@ -40,23 +72,26 @@
       ExpectLifetimeTransactions(in.size()));
 
   for (const auto& value : in) {
-    EXPECT_OK(SampleData::transact(binder, kCode,
-                                   [&](AParcel* in) {
-                                     EXPECT_OK(write(in, value));
-                                     return STATUS_OK;
-                                   },
-                                   [&](const AParcel* out) {
-                                     T readTarget;
-                                     EXPECT_OK(read(out, &readTarget));
-                                     EXPECT_EQ(value, readTarget);
-                                     return STATUS_OK;
-                                   }));
+    EXPECT_OK(SampleData::transact(
+        binder, kCode,
+        [&](AParcel* in) {
+          EXPECT_OK(write(in, value));
+          return STATUS_OK;
+        },
+        [&](const AParcel* out) {
+          T readTarget = {};
+          EXPECT_OK(read(out, &readTarget));
+          EXPECT_TRUE(NdkBinderSenseOfEquality<T>(value, readTarget))
+              << value << " is not " << readTarget;
+          return STATUS_OK;
+        }));
   }
 
   AIBinder_decStrong(binder);
 }
 
-template <typename T, binder_status_t (*write)(AParcel*, const T),
+template <typename T,
+          binder_status_t (*write)(AParcel*, typename WriteFrom<T>::type),
           binder_status_t (*read)(const AParcel*, T*)>
 void ExpectInOutMinMax() {
   ExpectInOut<T, write, read>(
@@ -107,6 +142,52 @@
   AIBinder_decStrong(binder);
 }
 
+TEST_F(NdkBinderTest_AParcel, StatusesInMustComeOut) {
+  // This does not clean up status objects.
+  ExpectInOut<AStatus*, AParcel_writeStatusHeader, AParcel_readStatusHeader>({
+      AStatus_newOk(),
+      AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT),
+      AStatus_fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+                                           "+++++++++[->++++++++<]>.+."),
+      AStatus_fromServiceSpecificError(1776),
+      AStatus_fromServiceSpecificErrorWithMessage(0xBEA, "utiful!"),
+  });
+}
+
+TEST_F(NdkBinderTest_AParcel, LowLevelErrorsHaveNoStatusHeader) {
+  AIBinder* binder =
+      SampleData::newBinder(nullptr, ExpectLifetimeTransactions(0));
+
+  EXPECT_EQ(
+      STATUS_UNKNOWN_ERROR,
+      SampleData::transact(binder, kCode, [&](AParcel* in) {
+        AStatus* status = nullptr;
+
+        status = AStatus_fromExceptionCode(EX_TRANSACTION_FAILED);
+        EXPECT_EQ(STATUS_FAILED_TRANSACTION,
+                  AParcel_writeStatusHeader(in, status));
+        AStatus_delete(status);
+
+        status = AStatus_fromExceptionCodeWithMessage(EX_TRANSACTION_FAILED,
+                                                      "something or other");
+        EXPECT_EQ(STATUS_FAILED_TRANSACTION,
+                  AParcel_writeStatusHeader(in, status));
+        AStatus_delete(status);
+
+        status = AStatus_fromStatus(STATUS_UNKNOWN_ERROR);
+        EXPECT_EQ(STATUS_UNKNOWN_ERROR, AParcel_writeStatusHeader(in, status));
+        AStatus_delete(status);
+
+        status = AStatus_fromStatus(STATUS_BAD_VALUE);
+        EXPECT_EQ(STATUS_BAD_VALUE, AParcel_writeStatusHeader(in, status));
+        AStatus_delete(status);
+
+        return STATUS_UNKNOWN_ERROR;
+      }));
+
+  AIBinder_decStrong(binder);
+}
+
 TEST_F(NdkBinderTest_AParcel, WhatGoesInMustComeOut) {
   ExpectInOut<int32_t, AParcel_writeInt32, AParcel_readInt32>(
       {-7, -1, 0, 1, 45});
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_status.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_status.cpp
new file mode 100644
index 0000000..e0d4244
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/android_binder_cts_status.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "Cts-NdkBinderTest"
+
+#include <android/binder_status.h>
+#include <gtest/gtest.h>
+
+#include <set>
+
+// clang-format off
+const static std::set<binder_status_t> kErrorStatuses = {
+  STATUS_UNKNOWN_ERROR,
+  STATUS_NO_MEMORY,
+  STATUS_INVALID_OPERATION,
+  STATUS_BAD_VALUE,
+  STATUS_BAD_TYPE,
+  STATUS_NAME_NOT_FOUND,
+  STATUS_PERMISSION_DENIED,
+  STATUS_NO_INIT,
+  STATUS_ALREADY_EXISTS,
+  STATUS_DEAD_OBJECT,
+  STATUS_FAILED_TRANSACTION,
+  STATUS_BAD_INDEX,
+  STATUS_NOT_ENOUGH_DATA,
+  STATUS_WOULD_BLOCK,
+  STATUS_TIMED_OUT,
+  STATUS_UNKNOWN_TRANSACTION,
+  STATUS_FDS_NOT_ALLOWED,
+  STATUS_UNEXPECTED_NULL
+};
+// Not in the API or above list
+const static std::set<binder_status_t> kUnknownStatuses = { -77, 1, 404, EX_TRANSACTION_FAILED };
+
+const static std::set<binder_exception_t> kErrorExceptions = {
+  EX_SECURITY,
+  EX_BAD_PARCELABLE,
+  EX_ILLEGAL_ARGUMENT,
+  EX_NULL_POINTER,
+  EX_ILLEGAL_STATE,
+  EX_NETWORK_MAIN_THREAD,
+  EX_UNSUPPORTED_OPERATION,
+  EX_SERVICE_SPECIFIC,
+  EX_PARCELABLE,
+  EX_TRANSACTION_FAILED
+};
+// Not in the API or above list
+const static std::set<binder_exception_t> kUnknownExceptions = { -77, 1, 404, STATUS_UNKNOWN_ERROR };
+// clang-format on
+
+// Checks the various attributes expected for an okay status.
+static void checkIsOkay(const AStatus* status) {
+  EXPECT_TRUE(AStatus_isOk(status));
+  EXPECT_EQ(std::string(), AStatus_getMessage(status));
+  EXPECT_EQ(EX_NONE, AStatus_getExceptionCode(status));
+  EXPECT_EQ(0, AStatus_getServiceSpecificError(status));
+  EXPECT_EQ(STATUS_OK, AStatus_getStatus(status));
+}
+static void checkIsErrorException(const AStatus* status,
+                                  binder_exception_t exception,
+                                  const std::string& message) {
+  EXPECT_FALSE(AStatus_isOk(status));
+  EXPECT_EQ(message, AStatus_getMessage(status));
+  EXPECT_EQ(exception, AStatus_getExceptionCode(status));
+  // not a service-specific error, so other errorcodes return the default
+  EXPECT_EQ(0, AStatus_getServiceSpecificError(status));
+  EXPECT_EQ(STATUS_OK, AStatus_getStatus(status));
+}
+static void checkIsServiceSpecific(const AStatus* status, int32_t error,
+                                   const std::string& message) {
+  EXPECT_FALSE(AStatus_isOk(status));
+  EXPECT_EQ(message, AStatus_getMessage(status));
+  EXPECT_EQ(EX_SERVICE_SPECIFIC, AStatus_getExceptionCode(status));
+  EXPECT_EQ(error, AStatus_getServiceSpecificError(status));
+  // not a service-specific error, so other errorcodes return the default
+  EXPECT_EQ(STATUS_OK, AStatus_getStatus(status));
+}
+static void checkIsErrorStatus(const AStatus* status, binder_status_t statusT) {
+  EXPECT_FALSE(AStatus_isOk(status));
+  EXPECT_EQ(std::string(), AStatus_getMessage(status));
+  EXPECT_EQ(EX_TRANSACTION_FAILED, AStatus_getExceptionCode(status));
+  EXPECT_EQ(statusT, AStatus_getStatus(status));
+  // not a service-specific error, so other errorcodes return the default
+  EXPECT_EQ(0, AStatus_getServiceSpecificError(status));
+}
+
+TEST(NdkBinderTest_AStatus, OkIsOk) {
+  AStatus* status = AStatus_newOk();
+  checkIsOkay(status);
+  AStatus_delete(status);
+}
+
+TEST(NdkBinderTest_AStatus, NoExceptionIsOkay) {
+  AStatus* status = AStatus_fromExceptionCode(EX_NONE);
+  checkIsOkay(status);
+  AStatus_delete(status);
+}
+
+TEST(NdkBinderTest_AStatus, StatusOkIsOkay) {
+  AStatus* status = AStatus_fromStatus(STATUS_OK);
+  checkIsOkay(status);
+  AStatus_delete(status);
+}
+
+TEST(NdkBinderTest_AStatus, ExceptionIsNotOkay) {
+  for (binder_exception_t exception : kErrorExceptions) {
+    AStatus* status = AStatus_fromExceptionCode(exception);
+    checkIsErrorException(status, exception, "" /*message*/);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, ExceptionWithMessageIsNotOkay) {
+  const std::string kMessage = "Something arbitrary.";
+  for (binder_exception_t exception : kErrorExceptions) {
+    AStatus* status =
+        AStatus_fromExceptionCodeWithMessage(exception, kMessage.c_str());
+    checkIsErrorException(status, exception, kMessage);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, ServiceSpecificIsNotOkay) {
+  for (int32_t error : {-404, -1, 0, 1, 23, 918}) {
+    AStatus* status = AStatus_fromServiceSpecificError(error);
+    checkIsServiceSpecific(status, error, "" /*message*/);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, ServiceSpecificWithMessageIsNotOkay) {
+  const std::string kMessage = "Something also arbitrary.";
+  for (int32_t error : {-404, -1, 0, 1, 23, 918}) {
+    AStatus* status =
+        AStatus_fromServiceSpecificErrorWithMessage(error, kMessage.c_str());
+    checkIsServiceSpecific(status, error, kMessage);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, StatusIsNotOkay) {
+  for (binder_status_t statusT : kErrorStatuses) {
+    AStatus* status = AStatus_fromStatus(statusT);
+    checkIsErrorStatus(status, statusT);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, ExceptionsPruned) {
+  for (binder_exception_t exception : kUnknownExceptions) {
+    EXPECT_EQ(kErrorExceptions.find(exception), kErrorExceptions.end())
+        << exception;
+
+    AStatus* status = AStatus_fromExceptionCode(exception);
+    checkIsErrorException(status, EX_TRANSACTION_FAILED, "" /*message*/);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, ExceptionsPrunedWithMessage) {
+  const std::string kMessage = "Something else arbitrary.";
+  for (binder_exception_t exception : kUnknownExceptions) {
+    EXPECT_EQ(kErrorExceptions.find(exception), kErrorExceptions.end())
+        << exception;
+
+    AStatus* status =
+        AStatus_fromExceptionCodeWithMessage(exception, kMessage.c_str());
+    checkIsErrorException(status, EX_TRANSACTION_FAILED, kMessage);
+    AStatus_delete(status);
+  }
+}
+
+TEST(NdkBinderTest_AStatus, StatusesPruned) {
+  for (binder_status_t statusT : kUnknownStatuses) {
+    EXPECT_EQ(kErrorStatuses.find(statusT), kErrorStatuses.end()) << statusT;
+
+    AStatus* status = AStatus_fromStatus(statusT);
+    checkIsErrorStatus(status, STATUS_UNKNOWN_ERROR);
+    AStatus_delete(status);
+  }
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp
index 714d66f..fad9f6f 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.cpp
@@ -21,10 +21,11 @@
 #include <android/log.h>
 
 static size_t sNumInstances = 0;
-size_t SampleData::numInstances() { return sNumInstances; }
+size_t ThisShouldBeDestroyed::numInstances() { return sNumInstances; }
+ThisShouldBeDestroyed::ThisShouldBeDestroyed() { sNumInstances++; }
+ThisShouldBeDestroyed::~ThisShouldBeDestroyed() { sNumInstances--; }
 
 void* SampleClassOnCreate(void* args) {
-  sNumInstances++;
   return args;  // SampleData
 }
 
@@ -33,7 +34,6 @@
   if (data->onDestroy != nullptr) {
     data->onDestroy(data);
   }
-  sNumInstances--;
   delete data;
 }
 
@@ -60,3 +60,15 @@
 const AIBinder_Class* SampleData::kAnotherClass =
     AIBinder_Class_define(SampleData::kAnotherDescriptor, SampleClassOnCreate,
                           SampleClassOnDestroy, SampleClassOnTransact);
+
+JNIEnv* GetEnv() {
+  JavaVM* vm = GetJavaVM();
+  if (vm == nullptr) return nullptr;
+
+  JNIEnv* result = nullptr;
+  jint attach = vm->AttachCurrentThread(&result, nullptr);
+
+  EXPECT_EQ(JNI_OK, attach);
+  EXPECT_NE(nullptr, result);
+  return result;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h
index 35fe38f..9d71afe 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/utilities.h
@@ -16,6 +16,7 @@
 
 #include <android/binder_ibinder.h>
 #include <gtest/gtest.h>
+#include <nativetesthelper_jni/utils.h>
 
 #include <functional>
 
@@ -47,9 +48,15 @@
   return STATUS_OK;
 }
 
-struct SampleData {
+// There is an assert instances of this class are destroyed in NdkBinderTest
+struct ThisShouldBeDestroyed {
   static size_t numInstances();
 
+  ThisShouldBeDestroyed();
+  virtual ~ThisShouldBeDestroyed();
+};
+
+struct SampleData : ThisShouldBeDestroyed {
   static const char* kDescriptor;
   static const AIBinder_Class* kClass;
 
@@ -118,6 +125,13 @@
 
 class NdkBinderTest : public ::testing::Test {
  public:
-  void SetUp() override { EXPECT_EQ(0, SampleData::numInstances()); }
-  void TearDown() override { EXPECT_EQ(0, SampleData::numInstances()); }
+  void SetUp() override { instances = ThisShouldBeDestroyed::numInstances(); }
+  void TearDown() override {
+    EXPECT_EQ(instances, ThisShouldBeDestroyed::numInstances());
+  }
+
+ private:
+  size_t instances = 0;
 };
+
+JNIEnv* GetEnv();
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
index 77d2f82..9d7bc93 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
@@ -178,8 +178,6 @@
             Log.d(TAG, "onError: " + String.valueOf(error));
             mNetworkScanStatus = EVENT_NETWORK_SCAN_ERROR;
             mErrorCode = error;
-            Log.d(TAG, "Stop the network scan");
-            mNetworkScan.stopScan();
             setReady(true);
         }
     }
diff --git a/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp b/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
index c098f5f..bf7aacc 100644
--- a/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
+++ b/tests/tests/media/libimagereaderjni/AImageReaderCts.cpp
@@ -348,7 +348,7 @@
             // Verity that outFenceFd's value will be changed by
             // AImageReader_acquireNextImageAsync.
             ret = AImageReader_acquireNextImageAsync(reader, &outImage, &outFenceFd);
-            if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd != kDummyFenceFd) {
+            if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd == 0) {
                 ALOGE("Failed to acquire image, ret=%d, outIamge=%p, outFenceFd=%d.", ret, outImage,
                       outFenceFd);
                 return;
diff --git a/tests/tests/telephony/AndroidManifest.xml b/tests/tests/telephony/AndroidManifest.xml
index b6840a3..764daf5 100644
--- a/tests/tests/telephony/AndroidManifest.xml
+++ b/tests/tests/telephony/AndroidManifest.xml
@@ -155,6 +155,8 @@
                    android:value="android.telephony.cts.embmstestapp/.CtsStreamingService"/>
         <meta-data android:name="mbms-download-service-override"
                    android:value="android.telephony.cts.embmstestapp/.CtsDownloadService"/>
+        <meta-data android:name="mbms-group-call-service-override"
+                   android:value="android.telephony.cts.embmstestapp/.CtsGroupCallService"/>
         <meta-data
             android:name="mbms-file-provider-authority"
             android:value="android.telephony.mbms.cts"/>
diff --git a/tests/tests/telephony/EmbmsMiddlewareTestApp/AndroidManifest.xml b/tests/tests/telephony/EmbmsMiddlewareTestApp/AndroidManifest.xml
index 0ea1be2..880faf6 100644
--- a/tests/tests/telephony/EmbmsMiddlewareTestApp/AndroidManifest.xml
+++ b/tests/tests/telephony/EmbmsMiddlewareTestApp/AndroidManifest.xml
@@ -30,6 +30,13 @@
         <action android:name="android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE" />
       </intent-filter>
     </service>
+    <service android:name="android.telephony.cts.embmstestapp.CtsGroupCallService"
+             android:launchMode="singleInstance">
+      <intent-filter>
+        <action android:name="android.telephony.action.EmbmsGroupCall" />
+        <action android:name="android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE" />
+      </intent-filter>
+    </service>
     <service android:name="android.telephony.cts.embmstestapp.CtsDownloadService"
              android:launchMode="singleInstance">
       <intent-filter>
diff --git a/tests/tests/telephony/EmbmsMiddlewareTestApp/aidl/android/telephony/cts/embmstestapp/ICtsGroupCallMiddlewareControl.aidl b/tests/tests/telephony/EmbmsMiddlewareTestApp/aidl/android/telephony/cts/embmstestapp/ICtsGroupCallMiddlewareControl.aidl
new file mode 100644
index 0000000..7f3bac7
--- /dev/null
+++ b/tests/tests/telephony/EmbmsMiddlewareTestApp/aidl/android/telephony/cts/embmstestapp/ICtsGroupCallMiddlewareControl.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.telephony.cts.embmstestapp;
+
+import android.os.Parcel;
+
+interface ICtsGroupCallMiddlewareControl {
+    // Resets the state of the CTS middleware
+    void reset();
+    // Get a list of calls made to the middleware binder.
+    // Looks like List<List<Object>>, where the first Object is always a String corresponding to
+    // the method name.
+    List getGroupCallSessionCalls();
+    // Force all methods that can return an error to return this error.
+    void forceErrorCode(int error);
+    // Fire the error callback on the current active call
+    void fireErrorOnGroupCall(int errorCode, String message);
+    // Fire the error callback on the group call session
+    void fireErrorOnSession(int errorCode, String message);
+    // The following fire callbacks on the active group call, using the provided arguments
+    void fireGroupCallStateChanged(int state, int reason);
+    void fireBroadcastSignalStrengthUpdated(int signalStrength);
+    // The following fire callbacks on the group call session.
+    void fireAvailableSaisUpdated(in List currentSais, in List availableSais);
+    void fireServiceInterfaceAvailable(String interfaceName, int index);
+}
\ No newline at end of file
diff --git a/tests/tests/telephony/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsGroupCallService.java b/tests/tests/telephony/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsGroupCallService.java
new file mode 100644
index 0000000..fade9fc
--- /dev/null
+++ b/tests/tests/telephony/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsGroupCallService.java
@@ -0,0 +1,221 @@
+/*
+ * 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.telephony.cts.embmstestapp;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCall;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.vendor.MbmsGroupCallServiceBase;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class CtsGroupCallService extends MbmsGroupCallServiceBase {
+    private static final Set<String> ALLOWED_PACKAGES = new HashSet<String>() {{
+        add("android.telephony.cts");
+    }};
+    private static final String TAG = "EmbmsTestGroupCall";
+
+    public static final String METHOD_INITIALIZE = "initialize";
+    public static final String METHOD_START_GROUP_CALL = "startGroupCall";
+    public static final String METHOD_UPDATE_GROUP_CALL = "updateGroupCall";
+    public static final String METHOD_STOP_GROUP_CALL = "stopGroupCall";
+    public static final String METHOD_CLOSE = "close";
+
+    public static final String CONTROL_INTERFACE_ACTION =
+            "android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE";
+    public static final ComponentName CONTROL_INTERFACE_COMPONENT =
+            ComponentName.unflattenFromString(
+                    "android.telephony.cts.embmstestapp/.CtsGroupCallService");
+
+    private MbmsGroupCallSessionCallback mAppCallback;
+    private GroupCallCallback mGroupCallCallback;
+
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private List<List> mReceivedCalls = new LinkedList<>();
+    private int mErrorCodeOverride = MbmsErrors.SUCCESS;
+
+    @Override
+    public int initialize(MbmsGroupCallSessionCallback callback, int subId) {
+        mReceivedCalls.add(Arrays.asList(METHOD_INITIALIZE, subId));
+        if (mErrorCodeOverride != MbmsErrors.SUCCESS) {
+            return mErrorCodeOverride;
+        }
+
+        int packageUid = Binder.getCallingUid();
+        String[] packageNames = getPackageManager().getPackagesForUid(packageUid);
+        if (packageNames == null) {
+            return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED;
+        }
+        boolean isUidAllowed = Arrays.stream(packageNames).anyMatch(ALLOWED_PACKAGES::contains);
+        if (!isUidAllowed) {
+            return MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED;
+        }
+
+        mHandler.post(() -> {
+            if (mAppCallback == null) {
+                mAppCallback = callback;
+            } else {
+                callback.onError(
+                        MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, "");
+                return;
+            }
+            callback.onMiddlewareReady();
+        });
+        return MbmsErrors.SUCCESS;
+    }
+
+    @Override
+    public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
+                final int[] frequencyArray, final GroupCallCallback callback) {
+        mReceivedCalls.add(Arrays.asList(METHOD_START_GROUP_CALL, subscriptionId, tmgi, saiArray,
+                frequencyArray));
+        if (mErrorCodeOverride != MbmsErrors.SUCCESS) {
+            return mErrorCodeOverride;
+        }
+
+        mGroupCallCallback = callback;
+        mHandler.post(() -> callback.onGroupCallStateChanged(GroupCall.STATE_STARTED,
+                GroupCall.REASON_BY_USER_REQUEST));
+        return MbmsErrors.SUCCESS;
+    }
+
+    @Override
+    public void updateGroupCall(int subscriptionId, long tmgi,
+            int[] saiArray, int[] frequencyArray) {
+        mReceivedCalls.add(Arrays.asList(METHOD_UPDATE_GROUP_CALL,
+                subscriptionId, tmgi, saiArray, frequencyArray));
+    }
+
+    @Override
+    public void stopGroupCall(int subscriptionId, long tmgi) {
+        mReceivedCalls.add(Arrays.asList(METHOD_STOP_GROUP_CALL, subscriptionId, tmgi));
+    }
+
+    @Override
+    public void dispose(int subscriptionId) {
+        mReceivedCalls.add(Arrays.asList(METHOD_CLOSE, subscriptionId));
+    }
+
+    @Override
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+        mAppCallback = null;
+    }
+
+    private final IBinder mControlInterface = new ICtsGroupCallMiddlewareControl.Stub() {
+        @Override
+        public void reset() {
+            mReceivedCalls.clear();
+            mHandler.removeCallbacksAndMessages(null);
+            mAppCallback = null;
+            mErrorCodeOverride = MbmsErrors.SUCCESS;
+        }
+
+        @Override
+        public List getGroupCallSessionCalls() {
+            return mReceivedCalls;
+        }
+
+        @Override
+        public void forceErrorCode(int error) {
+            mErrorCodeOverride = error;
+        }
+
+        @Override
+        public void fireErrorOnGroupCall(int errorCode, String message) {
+            mHandler.post(() -> mGroupCallCallback.onError(errorCode, message));
+        }
+
+        @Override
+        public void fireErrorOnSession(int errorCode, String message) {
+            mHandler.post(() -> mAppCallback.onError(errorCode, message));
+        }
+
+        @Override
+        public void fireGroupCallStateChanged(int state, int reason) {
+            mHandler.post(() -> mGroupCallCallback.onGroupCallStateChanged(state, reason));
+        }
+
+        @Override
+        public void fireBroadcastSignalStrengthUpdated(int signalStrength) {
+            mHandler.post(
+                    () -> mGroupCallCallback.onBroadcastSignalStrengthUpdated(signalStrength));
+        }
+
+        @Override
+        public void fireAvailableSaisUpdated(List currentSais, List availableSais) {
+            mHandler.post(() -> mAppCallback.onAvailableSaisUpdated(currentSais, availableSais));
+        }
+
+        @Override
+        public void fireServiceInterfaceAvailable(String interfaceName, int index) {
+            mHandler.post(() -> mAppCallback.onServiceInterfaceAvailable(interfaceName, index));
+        }
+    };
+
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quitSafely();
+        logd("CtsGroupCallService onDestroy");
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        logd("CtsGroupCallService onBind");
+        if (CONTROL_INTERFACE_ACTION.equals(intent.getAction())) {
+            logd("CtsGroupCallService control interface bind");
+            return mControlInterface;
+        }
+        IBinder binder = super.onBind(intent);
+
+        if (mHandlerThread != null && mHandlerThread.isAlive()) {
+            return binder;
+        }
+
+        mHandlerThread = new HandlerThread("CtsGroupCallServiceWorker");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        return binder;
+    }
+
+    private static void logd(String s) {
+        Log.d(TAG, s);
+    }
+
+    private void checkInitialized() {
+        if (mAppCallback == null) {
+            throw new IllegalStateException("Not yet initialized");
+        }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallSessionTest.java b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallSessionTest.java
new file mode 100644
index 0000000..becebdb
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallSessionTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.telephony.embms.cts;
+
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.cts.embmstestapp.CtsGroupCallService;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.MbmsErrors;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MbmsGroupCallSessionTest extends MbmsGroupCallTestBase {
+    public void testDuplicateSession() throws Exception {
+        try {
+            MbmsGroupCallSession failure = MbmsGroupCallSession.create(
+                    mContext, mCallbackExecutor, mCallback);
+            fail("Duplicate create should've thrown an exception");
+        } catch (IllegalStateException e) {
+            // Succeed
+        }
+    }
+
+    public void testClose() throws Exception {
+        mGroupCallSession.close();
+
+        // Make sure we can't use it anymore
+        try {
+            mGroupCallSession.startGroupCall(mCallbackExecutor, 0, new int[1], new int[1],
+                    new GroupCallCallback());
+            fail("GroupCall session should not be usable after close");
+        } catch (IllegalStateException e) {
+            // Succeed
+        }
+
+        // Make sure that the middleware got the call to close
+        List<List<Object>> closeCalls = getMiddlewareCalls(CtsGroupCallService.METHOD_CLOSE);
+        assertEquals(1, closeCalls.size());
+    }
+
+    public void testErrorDelivery() throws Exception {
+        mMiddlewareControl.forceErrorCode(
+                MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE);
+        mGroupCallSession.startGroupCall(mCallbackExecutor, 0, new int[1], new int[1],
+                new GroupCallCallback());
+        assertEquals(MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+                mCallback.waitOnError().arg1);
+    }
+
+    public void testCallbacks() throws Exception {
+        List<Integer> expectCurrentSais = Arrays.asList(10, 14, 17);
+        List<List<Integer>> expectAvailableSais = new ArrayList<List<Integer>>() {{
+            add(expectCurrentSais);
+            add(Arrays.asList(11, 15, 17));
+        }};
+        mMiddlewareControl.fireAvailableSaisUpdated(expectCurrentSais, expectAvailableSais);
+        SomeArgs callbackResult = mCallback.waitOnAvailableSaisUpdatedCalls();
+        assertEquals(callbackResult.arg1, expectCurrentSais);
+        assertEquals(callbackResult.arg2, expectAvailableSais);
+
+        String interfaceName = "TEST";
+        int index = 10;
+        mMiddlewareControl.fireServiceInterfaceAvailable(interfaceName, index);
+        callbackResult = mCallback.waitOnServiceInterfaceAvailableCalls();
+        assertEquals(interfaceName, callbackResult.arg1);
+        assertEquals(index, callbackResult.arg2);
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTest.java b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTest.java
new file mode 100644
index 0000000..7c5ac45
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.telephony.embms.cts;
+
+import android.annotation.Nullable;
+import android.telephony.cts.embmstestapp.CtsGroupCallService;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.GroupCall;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MbmsGroupCallTest extends MbmsGroupCallTestBase {
+    private class TestGroupCallCallback extends GroupCallCallback {
+        private final BlockingQueue<SomeArgs> mErrorCalls = new LinkedBlockingQueue<>();
+        private final BlockingQueue<SomeArgs> mGroupCallStateChangedCalls=
+                new LinkedBlockingQueue<>();
+        private final BlockingQueue<SomeArgs> mBroadcastSignalStrengthUpdatedCalls =
+                new LinkedBlockingQueue<>();
+
+        @Override
+        public void onError(int errorCode, @Nullable String message) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = errorCode;
+            args.arg2 = message;
+            mErrorCalls.add(args);
+        }
+
+        @Override
+        public void onGroupCallStateChanged(int state, int reason) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = state;
+            args.arg2 = reason;
+            mGroupCallStateChangedCalls.add(args);
+        }
+
+        @Override
+        public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = signalStrength;
+            mBroadcastSignalStrengthUpdatedCalls.add(args);
+        }
+
+        public SomeArgs waitOnError() {
+            try {
+                return mErrorCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+
+        public SomeArgs waitOnGroupCallStateChanged() {
+            try {
+                return mGroupCallStateChangedCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+
+        public SomeArgs waitOnBroadcastSignalStrengthUpdated() {
+            try {
+                return mBroadcastSignalStrengthUpdatedCalls.poll(
+                        ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+    }
+
+    private static final long TMGI = 568734963245L;
+    private static final int[] SAI_ARRAY = new int[]{16, 24, 46, 76};
+    private static final int[] FREQUENCY_ARRAY = new int[]{2075, 2050, 1865};
+
+    private TestGroupCallCallback mGroupCallCallback =
+            new TestGroupCallCallback();
+
+    public void testStartGroupCall() throws Exception {
+        GroupCall groupCall = mGroupCallSession.startGroupCall(mCallbackExecutor,
+                TMGI, SAI_ARRAY, FREQUENCY_ARRAY, mGroupCallCallback);
+        assertNotNull(groupCall);
+        assertEquals(TMGI, groupCall.getTmgi());
+
+        SomeArgs args = mGroupCallCallback.waitOnGroupCallStateChanged();
+        assertEquals(GroupCall.STATE_STARTED, args.arg1);
+        assertEquals(GroupCall.REASON_BY_USER_REQUEST, args.arg2);
+
+        List<List<Object>> startGroupCallCalls =
+                getMiddlewareCalls(CtsGroupCallService.METHOD_START_GROUP_CALL);
+        assertEquals(1, startGroupCallCalls.size());
+        List<Object> startGroupCallCall = startGroupCallCalls.get(0);
+        assertEquals(TMGI, startGroupCallCall.get(2));
+        assertArrayEquals(SAI_ARRAY, (int[]) startGroupCallCall.get(3));
+        assertArrayEquals(FREQUENCY_ARRAY, (int[]) startGroupCallCall.get(4));
+    }
+
+    public void testUpdateGroupCall() throws Exception {
+        GroupCall groupCall = mGroupCallSession.startGroupCall(mCallbackExecutor,
+                TMGI, SAI_ARRAY, FREQUENCY_ARRAY, mGroupCallCallback);
+        int[] newSais = new int[]{16};
+        int[] newFreqs = new int[]{2075};
+        groupCall.updateGroupCall(newSais, newFreqs);
+
+        List<List<Object>> updateGroupCallCalls =
+                getMiddlewareCalls(CtsGroupCallService.METHOD_UPDATE_GROUP_CALL);
+        assertEquals(1, updateGroupCallCalls.size());
+        List<Object> updateGroupCallCall = updateGroupCallCalls.get(0);
+        assertEquals(TMGI, updateGroupCallCall.get(2));
+        assertArrayEquals(newSais, (int[]) updateGroupCallCall.get(3));
+        assertArrayEquals(newFreqs, (int[]) updateGroupCallCall.get(4));
+    }
+
+    public void testStopGroupCall() throws Exception {
+        GroupCall groupCall = mGroupCallSession.startGroupCall(mCallbackExecutor,
+                TMGI, SAI_ARRAY, FREQUENCY_ARRAY, mGroupCallCallback);
+        groupCall.close();
+        List<List<Object>> stopGroupCallCalls =
+                getMiddlewareCalls(CtsGroupCallService.METHOD_STOP_GROUP_CALL);
+        assertEquals(1, stopGroupCallCalls.size());
+        assertEquals(TMGI, stopGroupCallCalls.get(0).get(2));
+    }
+
+    public void testGroupCallCallbacks() throws Exception {
+        mGroupCallSession.startGroupCall(mCallbackExecutor,
+                TMGI, SAI_ARRAY, FREQUENCY_ARRAY, mGroupCallCallback);
+        mMiddlewareControl.fireErrorOnGroupCall(MbmsErrors.GeneralErrors.ERROR_IN_E911,
+                MbmsGroupCallTest.class.getSimpleName());
+        SomeArgs groupCallErrorArgs = mGroupCallCallback.waitOnError();
+        assertEquals(MbmsErrors.GeneralErrors.ERROR_IN_E911, groupCallErrorArgs.arg1);
+        assertEquals(MbmsGroupCallTest.class.getSimpleName(), groupCallErrorArgs.arg2);
+
+        int broadcastSignalStrength = 3;
+        mMiddlewareControl.fireBroadcastSignalStrengthUpdated(broadcastSignalStrength);
+        assertEquals(broadcastSignalStrength,
+                mGroupCallCallback.waitOnBroadcastSignalStrengthUpdated().arg1);
+    }
+
+    public void testStartGroupCallFailure() throws Exception {
+        mMiddlewareControl.forceErrorCode(
+                MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE);
+        mGroupCallSession.startGroupCall(mCallbackExecutor,
+                TMGI, SAI_ARRAY, FREQUENCY_ARRAY, mGroupCallCallback);
+        assertEquals(MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+                mCallback.waitOnError().arg1);
+    }
+
+    private void assertArrayEquals(int[] expected, int[] actual) {
+        assertEquals(expected.length, actual.length);
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                throw new AssertionError("Arrays differ at element " + i
+                        + " -- expected: " + Arrays.toString(expected) + "; actual: "
+                        + Arrays.toString(actual));
+            }
+        }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTestBase.java b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTestBase.java
new file mode 100644
index 0000000..ba51e56
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/embms/cts/MbmsGroupCallTestBase.java
@@ -0,0 +1,169 @@
+package android.telephony.embms.cts;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.cts.embmstestapp.CtsGroupCallService;
+import android.telephony.cts.embmstestapp.ICtsGroupCallMiddlewareControl;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.test.InstrumentationTestCase;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+public class MbmsGroupCallTestBase extends InstrumentationTestCase {
+    protected static final int ASYNC_TIMEOUT = 10000;
+
+    protected static class TestCallback extends MbmsGroupCallSessionCallback {
+        private final BlockingQueue<SomeArgs> mErrorCalls = new LinkedBlockingQueue<>();
+        private final BlockingQueue<SomeArgs> mOnAvailableSaisUpdatedCalls =
+                new LinkedBlockingQueue<>();
+        private final BlockingQueue<SomeArgs> mOnServiceInterfaceAvailableCalls =
+                new LinkedBlockingQueue<>();
+        private final BlockingQueue<SomeArgs> mMiddlewareReadyCalls = new LinkedBlockingQueue<>();
+        private int mNumErrorCalls = 0;
+
+        @Override
+        public void onError(int errorCode, @Nullable String message) {
+            mNumErrorCalls += 1;
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = errorCode;
+            args.arg2 = message;
+            mErrorCalls.add(args);
+        }
+
+        @Override
+        public void onMiddlewareReady() {
+            mMiddlewareReadyCalls.add(SomeArgs.obtain());
+        }
+
+        @Override
+        public void onAvailableSaisUpdated(List<Integer> currentSais,
+                List<List<Integer>> availableSais) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = currentSais;
+            args.arg2 = availableSais;
+            mOnAvailableSaisUpdatedCalls.add(args);
+        }
+
+        @Override
+        public void onServiceInterfaceAvailable(String interfaceName, int index) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = interfaceName;
+            args.arg2 = index;
+            mOnServiceInterfaceAvailableCalls.add(args);
+        }
+
+        public SomeArgs waitOnError() {
+            try {
+                return mErrorCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+
+        public SomeArgs waitOnAvailableSaisUpdatedCalls() {
+            try {
+                return mOnAvailableSaisUpdatedCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+
+        public SomeArgs waitOnServiceInterfaceAvailableCalls() {
+            try {
+                return mOnServiceInterfaceAvailableCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return null;
+            }
+        }
+
+        public boolean waitOnMiddlewareReady() {
+            try {
+                return mMiddlewareReadyCalls.poll(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS) != null;
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
+        public int getNumErrorCalls() {
+            return mNumErrorCalls;
+        }
+    }
+
+    Context mContext;
+    HandlerThread mHandlerThread;
+    Executor mCallbackExecutor;
+    ICtsGroupCallMiddlewareControl mMiddlewareControl;
+    MbmsGroupCallSession mGroupCallSession;
+    TestCallback mCallback = new TestCallback();
+
+    @Override
+    public void setUp() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mHandlerThread = new HandlerThread("EmbmsCtsTestWorker");
+        mHandlerThread.start();
+        mCallbackExecutor = (new Handler(mHandlerThread.getLooper()))::post;
+        mCallback = new TestCallback();
+        getControlBinder();
+        setupGroupCallSession();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        mGroupCallSession.close();
+        mMiddlewareControl.reset();
+    }
+
+    private void setupGroupCallSession() throws Exception {
+        mGroupCallSession = MbmsGroupCallSession.create(
+                mContext, mCallbackExecutor, mCallback);
+        assertNotNull(mGroupCallSession);
+        assertTrue(mCallback.waitOnMiddlewareReady());
+        assertEquals(0, mCallback.getNumErrorCalls());
+        List initializeCall = (List) mMiddlewareControl.getGroupCallSessionCalls().get(0);
+        assertEquals(CtsGroupCallService.METHOD_INITIALIZE, initializeCall.get(0));
+    }
+
+    private void getControlBinder() throws InterruptedException {
+        Intent bindIntent = new Intent(CtsGroupCallService.CONTROL_INTERFACE_ACTION);
+        bindIntent.setComponent(CtsGroupCallService.CONTROL_INTERFACE_COMPONENT);
+        final CountDownLatch bindLatch = new CountDownLatch(1);
+
+        boolean success = mContext.bindService(bindIntent, new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                mMiddlewareControl = ICtsGroupCallMiddlewareControl.Stub.asInterface(service);
+                bindLatch.countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                mMiddlewareControl = null;
+            }
+        }, Context.BIND_AUTO_CREATE);
+        if (!success) {
+            fail("Failed to get control interface -- bind error");
+        }
+        bindLatch.await(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+    }
+
+    protected List<List<Object>> getMiddlewareCalls(String methodName) throws RemoteException {
+        return ((List<List<Object>>) mMiddlewareControl.getGroupCallSessionCalls()).stream()
+                .filter((elem) -> elem.get(0).equals(methodName))
+                .collect(Collectors.toList());
+    }
+}
\ No newline at end of file