Merge "CTS test cases for IMS.setBackDisposition()"
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
index 51b8474..ab2cf84 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
@@ -102,19 +102,21 @@
                  rp.getVisibility() <= VISIBILITY_PUBLIC));
     }
 
-    // Tests default state: zen mode off, no suppressors
+    // Tests default state: zen mode is a valid/expected value
     public void testZenMode() throws Exception {
         final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
                 "dumpsys notification --proto");
         ZenModeProto zenProto = dump.getZen();
 
-        assertEquals(ZenMode.ZEN_MODE_OFF, zenProto.getZenMode());
-        assertEquals(0, zenProto.getEnabledActiveConditionsCount());
-
-        // b/64606626 Watches intentionally suppress notifications always
-        if (!getDevice().hasFeature(FEATURE_WATCH)) {
-            assertEquals(0, zenProto.getSuppressedEffects());
-            assertEquals(0, zenProto.getSuppressorsCount());
+        switch(zenProto.getZenMode()) {
+            case ZEN_MODE_OFF:
+            case ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+            case ZEN_MODE_NO_INTERRUPTIONS:
+            case ZEN_MODE_ALARMS:
+                break;
+            default:
+                fail("Unexpected ZenMode value");
+                break;
         }
 
         zenProto.getPolicy();
diff --git a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
index a0cfd07..f49b6ce 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.cts;
 
-import android.app.ProcessState;
+import android.app.ProcessStateEnum;
 import android.content.IntentProto;
 import android.os.BatteryManagerProto;
 import android.os.LooperProto;
@@ -75,12 +75,11 @@
         assertEquals(uid.getUid(), SYSTEM_UID);
         assertEquals(uid.getUidString(), Integer.toString(SYSTEM_UID));
         assertTrue(uid.getIsActive());
-        assertFalse(uid.getIsProcessStateUnknown());
 
         for (PowerManagerServiceDumpProto.UidStateProto us : dump.getUidStatesList()) {
             assertTrue(0 <= us.getUid());
             assertTrue(0 <= us.getNumWakeLocks());
-            assertTrue(ProcessState.getDescriptor()
+            assertTrue(ProcessStateEnum.getDescriptor()
                     .getValues()
                     .contains(us.getProcessState().getValueDescriptor()));
         }
diff --git a/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java b/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
index 52be162..e2408fc 100644
--- a/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
+++ b/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
@@ -15,6 +15,7 @@
 package android.jvmti.cts;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.NullOutputReceiver;
 import com.android.ddmlib.testrunner.ITestRunListener;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -64,7 +65,106 @@
         mAbi = arg0;
     }
 
-    public void testJvmtiAttach() throws Exception {
+    private static interface TestRun {
+        public void run(ITestDevice device, String pkg, String apk, String abiName);
+    }
+
+    private final static String AGENT = "libctsjvmtiattachagent.so";
+
+    public void testJvmtiAttachDuringBind() throws Exception {
+        runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
+            try {
+                runAttachTestCmd(device, pkg, "--attach-agent-bind " + AGENT);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed bind-time attaching", e);
+            }
+        });
+    };
+
+    public void testJvmtiAttachEarly() throws Exception {
+        runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
+            try {
+                String pwd = device.executeShellCommand("run-as " + pkg + " pwd");
+                if (pwd == null) {
+                    throw new RuntimeException("pwd failed");
+                }
+                pwd = pwd.trim();
+                if (pwd.isEmpty()) {
+                    throw new RuntimeException("pwd failed");
+                }
+
+                // Give it a different name, so we do not have "contamination" from
+                // the test APK.
+                String libInDataData = AGENT.substring(0, AGENT.length() - ".so".length())
+                        + "2.so";
+                String agentInDataData =
+                        installLibToDataData(device, pkg, abiName, apk, pwd, AGENT,
+                                libInDataData);
+                runAttachTestCmd(device, pkg, "--attach-agent " + agentInDataData);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed pre-bind attaching", e);
+            }
+        });
+    };
+
+    public void testJvmtiAgentAppInternal() throws Exception {
+        runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
+            try {
+                String setAgentAppCmd = "cmd activity set-agent-app " + pkg + " " + AGENT;
+                device.executeShellCommand(setAgentAppCmd);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed running set-agent-app", e);
+            }
+
+            try {
+                runAttachTestCmd(device, pkg, "");
+
+                // And again.
+                runAttachTestCmd(device, pkg, "");
+            } catch (Exception e) {
+                throw new RuntimeException("Failed agent-app attaching", e);
+            }
+        });
+    };
+
+    public void testJvmtiAgentAppExternal() throws Exception {
+        runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
+            try {
+                String pwd = device.executeShellCommand("run-as " + pkg + " pwd");
+                if (pwd == null) {
+                    throw new RuntimeException("pwd failed");
+                }
+                pwd = pwd.trim();
+                if (pwd.isEmpty()) {
+                    throw new RuntimeException("pwd failed");
+                }
+
+                // Give it a different name, so we do not have "contamination" from
+                // the test APK.
+                String libInDataData = AGENT.substring(0, AGENT.length() - ".so".length())
+                        + "2.so";
+                String agentInDataData =
+                        installLibToDataData(device, pkg, abiName, apk, pwd, AGENT,
+                                libInDataData);
+
+                String setAgentAppCmd = "cmd activity set-agent-app " + pkg + " " + agentInDataData;
+                device.executeShellCommand(setAgentAppCmd);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed running set-agent-app", e);
+            }
+
+            try {
+                runAttachTestCmd(device, pkg, "");
+
+                // And again.
+                runAttachTestCmd(device, pkg, "");
+            } catch (Exception e) {
+                throw new RuntimeException("Failed agent-app attaching", e);
+            }
+        });
+    };
+
+    private void runJvmtiAgentLoadTest(TestRun runner) throws Exception {
         final ITestDevice device = getDevice();
 
         String testingArch = AbiUtils.getBaseArchForAbi(mAbi.getName());
@@ -84,7 +184,7 @@
             throw new IllegalStateException("Incorrect configuration");
         }
 
-        runAttachTest(device, mTestPackageName, mTestApk);
+        runner.run(device, mTestPackageName, mTestApk, mAbi.getName());
     }
 
     private String getDeviceBaseArch(ITestDevice device) throws Exception {
@@ -93,50 +193,19 @@
         return AbiUtils.getBaseArchForAbi(abi);
     }
 
-    private void runAttachTestCmd(ITestDevice device, String pkg, String agentParams)
+    private static void runAttachTestCmd(ITestDevice device, String pkg, String agentParams)
             throws Exception {
         String attachCmd = "cmd activity start -S -W " + agentParams + " -n " + pkg
                 + "/android.jvmti.JvmtiActivity";
 
-        String attachReply = device.executeShellCommand(attachCmd);
         // Don't try to parse the output. The test will time out anyways if this didn't
         // work.
-        if (attachReply != null && !attachReply.trim().isEmpty()) {
-            CLog.e(attachReply);
-        }
+        device.executeShellCommand(attachCmd, NullOutputReceiver.getReceiver(), 10,
+                TimeUnit.SECONDS, 1);
     }
 
-    private final static String AGENT = "libctsjvmtiattachagent.so";
-
-    private void runAttachTest(ITestDevice device, String pkg, String apk) {
-        try {
-            runAttachTestCmd(device, pkg, "--attach-agent-bind " + AGENT);
-        } catch (Exception e) {
-            throw new RuntimeException("Failed bind-time attaching", e);
-        }
-        try {
-            String pwd = device.executeShellCommand("run-as " + pkg + " pwd");
-            if (pwd == null) {
-                throw new RuntimeException("pwd failed");
-            }
-            pwd = pwd.trim();
-            if (pwd.isEmpty()) {
-                throw new RuntimeException("pwd failed");
-            }
-
-            // Give it a different name, so we do not have "contamination" from
-            // the test APK.
-            String libInDataData = AGENT.substring(0, AGENT.length() - ".so".length()) + "2.so";
-            String agentInDataData =
-                    installLibToDataData(device, pkg, apk, pwd, AGENT, libInDataData);
-            runAttachTestCmd(device, pkg, "--attach-agent " + agentInDataData);
-        } catch (Exception e) {
-            throw new RuntimeException("Failed pre-bind attaching", e);
-        }
-    }
-
-    String installLibToDataData(ITestDevice device, String pkg, String apk, String dataData,
-            String library, String newLibName) throws Exception {
+    private String installLibToDataData(ITestDevice device, String pkg, String abiName,
+            String apk, String dataData, String library, String newLibName) throws Exception {
         ZipFile zf = null;
         File tmpFile = null;
         String libInTmp = null;
@@ -146,7 +215,7 @@
             File apkFile = mBuildHelper.getTestFile(apk);
             zf = new ZipFile(apkFile);
 
-            String libPathInApk = "lib/" + mAbi.getName() + "/" + library;
+            String libPathInApk = "lib/" + abiName + "/" + library;
             tmpFile = ZipUtil.extractFileFromZip(zf, libPathInApk);
 
             libInTmp = "/data/local/tmp/" + tmpFile.getName();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 1a7f42e0..3052937 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -15,7 +15,7 @@
  */
 package android.cts.statsd.atom;
 
-import android.app.ProcessState; // from atoms.proto's activitymanager.proto's enum.
+import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
 
 import com.android.os.AtomsProto.Atom;
 import com.android.os.StatsLog.EventMetricData;
@@ -64,37 +64,37 @@
     // The tests here are using the BatteryStats definition of 'background'.
     private static final Set<Integer> BG_STATES = new HashSet<>(
             Arrays.asList(
-                    ProcessState.PROCESS_STATE_IMPORTANT_BACKGROUND_VALUE,
-                    ProcessState.PROCESS_STATE_TRANSIENT_BACKGROUND_VALUE,
-                    ProcessState.PROCESS_STATE_BACKUP_VALUE,
-                    ProcessState.PROCESS_STATE_SERVICE_VALUE,
-                    ProcessState.PROCESS_STATE_RECEIVER_VALUE,
-                    ProcessState.PROCESS_STATE_HEAVY_WEIGHT_VALUE
+                    ProcessStateEnum.PROCESS_STATE_IMPORTANT_BACKGROUND_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_TRANSIENT_BACKGROUND_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_BACKUP_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_SERVICE_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_RECEIVER_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_HEAVY_WEIGHT_VALUE
             ));
 
     // Using the BatteryStats definition of 'cached', which is why HOME (etc) are considered cached.
     private static final Set<Integer> CACHED_STATES = new HashSet<>(
             Arrays.asList(
-                    ProcessState.PROCESS_STATE_HOME_VALUE,
-                    ProcessState.PROCESS_STATE_LAST_ACTIVITY_VALUE,
-                    ProcessState.PROCESS_STATE_CACHED_ACTIVITY_VALUE,
-                    ProcessState.PROCESS_STATE_CACHED_ACTIVITY_CLIENT_VALUE,
-                    ProcessState.PROCESS_STATE_CACHED_RECENT_VALUE,
-                    ProcessState.PROCESS_STATE_CACHED_EMPTY_VALUE
+                    ProcessStateEnum.PROCESS_STATE_HOME_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_LAST_ACTIVITY_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_CLIENT_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_CACHED_RECENT_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_CACHED_EMPTY_VALUE
             ));
 
     private static final Set<Integer> MISC_STATES = new HashSet<>(
             Arrays.asList(
-                    ProcessState.PROCESS_STATE_PERSISTENT_VALUE, // TODO: untested
-                    ProcessState.PROCESS_STATE_PERSISTENT_UI_VALUE, // TODO: untested
-                    ProcessState.PROCESS_STATE_TOP_VALUE,
-                    ProcessState.PROCESS_STATE_BOUND_FOREGROUND_SERVICE_VALUE, // TODO: untested
-                    ProcessState.PROCESS_STATE_FOREGROUND_SERVICE_VALUE,
-                    ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE,
-                    ProcessState.PROCESS_STATE_TOP_SLEEPING_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_PERSISTENT_VALUE, // TODO: untested
+                    ProcessStateEnum.PROCESS_STATE_PERSISTENT_UI_VALUE, // TODO: untested
+                    ProcessStateEnum.PROCESS_STATE_TOP_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_BOUND_FOREGROUND_SERVICE_VALUE, // TODO: untested
+                    ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE,
 
-                    ProcessState.PROCESS_STATE_UNKNOWN_VALUE,
-                    ProcessState.PROCESS_STATE_NONEXISTENT_VALUE
+                    ProcessStateEnum.PROCESS_STATE_UNKNOWN_VALUE,
+                    ProcessStateEnum.PROCESS_STATE_NONEXISTENT_VALUE
             ));
 
     private static final Set<Integer> ALL_STATES = Stream.of(MISC_STATES, CACHED_STATES, BG_STATES)
@@ -116,7 +116,7 @@
     public void testForegroundService() throws Exception {
         if (!TESTS_ENABLED) return;
         Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessState.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
+                ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
         Set<Integer> offStates = complement(onStates);
 
         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
@@ -135,7 +135,7 @@
     public void testForeground() throws Exception {
         if (!TESTS_ENABLED) return;
         Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
+                ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
         // There are no offStates, since the app remains in foreground until killed.
 
         List<Set<Integer>> stateSet = Arrays.asList(onStates); // state sets, in order
@@ -176,7 +176,7 @@
     public void testTop() throws Exception {
         if (!TESTS_ENABLED) return;
         Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessState.PROCESS_STATE_TOP_VALUE));
+                ProcessStateEnum.PROCESS_STATE_TOP_VALUE));
         Set<Integer> offStates = complement(onStates);
 
         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
@@ -197,7 +197,7 @@
     public void testTopSleeping() throws Exception {
         if (!TESTS_ENABLED) return;
         Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessState.PROCESS_STATE_TOP_SLEEPING_VALUE));
+                ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE));
         Set<Integer> offStates = complement(onStates);
 
         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
@@ -252,6 +252,11 @@
         assertStatesOccurred(stateSet, data, 1_000, PROC_STATE_FUNCTION);
     }
 
+    public void testValidityOfStates() throws Exception {
+        assertFalse("UNKNOWN_TO_PROTO should not be a valid state",
+                ALL_STATES.contains(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE));
+    }
+
     /**
      * Runs a (background) service to perform the given action.
      * @param actionValue the action code constants indicating the desired action to perform.
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
index 18bf397..56aedd0 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -72,7 +72,6 @@
     }
 
     @Presubmit
-    @FlakyTest(bugId = 71877849)
     @Test
     public void testChangeFontScaleRelaunch() throws Exception {
         // Should relaunch and receive no onConfigurationChanged()
@@ -80,7 +79,6 @@
     }
 
     @Presubmit
-    @FlakyTest(bugId = 71877849)
     @Test
     public void testChangeFontScaleNoRelaunch() throws Exception {
         // Should receive onConfigurationChanged() and no relaunch
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index 36e6f04..c5f13f1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -83,7 +83,6 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 71918731)
     public void testStackList() throws Exception {
         launchActivity(TEST_ACTIVITY_NAME);
         mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
@@ -235,7 +234,6 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 71918731)
     public void testLaunchToSideSingleInstance() throws Exception {
         launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
     }
@@ -245,7 +243,6 @@
         launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
     }
 
-    @FlakyTest
     @Presubmit
     @Test
     public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 89ff760..14c0179 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -53,7 +53,6 @@
  * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.CrossAppDragAndDropTests
  */
 @Presubmit
-@FlakyTest(bugId = 65739235)
 public class CrossAppDragAndDropTests {
     private static final String TAG = "CrossAppDragAndDrop";
 
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index eb6fecb..481c9a4 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -291,12 +291,20 @@
 
         <activity android:name="com.android.cts.content.StubActivity"/>
 
-        <service android:name="com.android.cts.content.SyncService">
+        <service android:name="com.android.cts.content.NotAlwaysSyncableSyncService">
             <intent-filter>
                 <action android:name="android.content.SyncAdapter"/>
             </intent-filter>
             <meta-data android:name="android.content.SyncAdapter"
-                android:resource="@xml/accountaccesssyncadapter" />
+                android:resource="@xml/not_always_syncable_account_access_adapter" />
+        </service>
+
+        <service android:name="com.android.cts.content.AlwaysSyncableSyncService">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/always_syncable_account_access_adapter" />
         </service>
 
     </application>
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
index 09523ba..14dd2c9 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
@@ -23,8 +23,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     ctstestrunner \
-    ub-uiautomator \
-    compatibility-device-util \
     accountaccesslib
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
index 398a75b..e1fd828 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
@@ -24,7 +24,7 @@
 
         <activity android:name=".StubActivity"/>
 
-        <service android:name=".SyncService">
+        <service android:name=".AlwaysSyncableSyncService">
             <intent-filter>
                 <action android:name="android.content.SyncAdapter"/>
             </intent-filter>
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index 3c791fd..8ad7c19 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -16,29 +16,29 @@
 
 package com.android.cts.content;
 
+import static com.android.cts.content.Utils.ALWAYS_SYNCABLE_AUTHORITY;
+import static com.android.cts.content.Utils.SYNC_TIMEOUT_MILLIS;
+import static com.android.cts.content.Utils.allowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.disallowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.getUiDevice;
+import static com.android.cts.content.Utils.hasDataConnection;
+import static com.android.cts.content.Utils.hasNotificationSupport;
+import static com.android.cts.content.Utils.isWatch;
+import static com.android.cts.content.Utils.requestSync;
+import static com.android.cts.content.Utils.withAccount;
+
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
 import android.content.SyncRequest;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
@@ -54,17 +54,13 @@
 import org.junit.runner.RunWith;
 
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.util.regex.Pattern;
 
-import com.android.compatibility.common.util.SystemUtil;
-
 /**
  * Tests whether a sync adapter can access accounts.
  */
 @RunWith(AndroidJUnit4.class)
 public class CtsSyncAccountAccessOtherCertTestCases {
-    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
     private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
     private static final String LOG_TAG =
             CtsSyncAccountAccessOtherCertTestCases.class.getSimpleName();
@@ -72,11 +68,13 @@
     private static final Pattern PERMISSION_REQUESTED = Pattern.compile(
             "Permission Requested|Permission requested");
     private static final Pattern ALLOW_SYNC = Pattern.compile("ALLOW|Allow");
-    public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
 
     @Rule
     public final TestRule mFlakyTestRule = new FlakyTestRule(3);
 
+    @Rule
+    public final ActivityTestRule<StubActivity> activity = new ActivityTestRule(StubActivity.class);
+
     @Before
     public void setUp() throws Exception {
         allowSyncAdapterRunInBackgroundAndDataInBackground();
@@ -96,38 +94,16 @@
         // the permission request will not trigger. b/72114924
         assumeFalse(ActivityManager.isRunningInTestHarness());
 
-        Intent intent = new Intent(getContext(), StubActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+        try (AutoCloseable ignored = withAccount(activity.getActivity())) {
+            AbstractThreadedSyncAdapter adapter = AlwaysSyncableSyncService.getInstance(
+                    activity.getActivity()).setNewDelegate();
 
-        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
-        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
-                null, null).getResult();
+            SyncRequest request = requestSync(ALWAYS_SYNCABLE_AUTHORITY);
+            Log.i(LOG_TAG, "Sync requested " + request);
 
-        Account addedAccount = new Account(
-                result.getString(AccountManager.KEY_ACCOUNT_NAME),
-                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
-        waitForSyncManagerAccountChangeUpdate();
-
-        try {
-            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
-
-            Bundle extras = new Bundle();
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
-            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
-            SyncRequest request = new SyncRequest.Builder()
-                    .setSyncAdapter(null, "com.android.cts.stub.provider")
-                    .syncOnce()
-                    .setExtras(extras)
-                    .setExpedited(true)
-                    .setManual(true)
-                    .build();
-            ContentResolver.requestSync(request);
-
-            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS).times(0)).onPerformSync(any(), any(),
-                    any(), any(), any());
+            Thread.sleep(SYNC_TIMEOUT_MILLIS);
+            verify(adapter, never()).onPerformSync(any(), any(), any(), any(), any());
+            Log.i(LOG_TAG, "Did not get onPerformSync");
 
             UiDevice uiDevice = getUiDevice();
             if (isWatch()) {
@@ -162,11 +138,7 @@
 
             verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
                     any());
-        } finally {
-            // Ask the differently signed authenticator to drop all accounts
-            accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
-                    null, false, null, null);
-            activity.finish();
+            Log.i(LOG_TAG, "Got onPerformSync");
         }
     }
 
@@ -198,54 +170,4 @@
             1 /* endY */,
             50 /* numberOfSteps */);
     }
-
-    private boolean isWatch() {
-        return (getContext().getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH;
-    }
-
-    private Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    private UiDevice getUiDevice() {
-        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-    }
-
-    private void waitForSyncManagerAccountChangeUpdate() {
-        // Wait for the sync manager to be notified for the new account.
-        // Unfortunately, there is no way to detect this event, sigh...
-        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
-    }
-
-    private boolean hasDataConnection() {
-        ConnectivityManager connectivityManager = getContext().getSystemService(
-                ConnectivityManager.class);
-        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
-        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
-    }
-
-    private boolean hasNotificationSupport() {
-        final PackageManager manager = getContext().getPackageManager();
-        return !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
-                && !manager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
-    }
-
-    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist +" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
-    }
-
-    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist -" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
-    }
 }
diff --git a/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml b/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
index 76c4fb4..a0dee84 100644
--- a/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
+++ b/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
@@ -35,6 +35,13 @@
             android:syncable="true">
         </provider>
 
+        <provider
+            android:name=".StubProvider2"
+            android:authorities="com.android.cts.stub.provider2"
+            android:exported="true"
+            android:syncable="true">
+        </provider>
+
     </application>
 
 </manifest>
diff --git a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
index eec9d21..0275fa9 100644
--- a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
+++ b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
@@ -30,6 +30,7 @@
 public class StubAuthenticator extends Service {
     public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
 
+    private static long sNumAccountsAdded;
     private Authenticator mAuthenticator;
 
     @Override
@@ -59,10 +60,14 @@
                 String accountType, String tokenType, String[] strings,
                 Bundle bundle) throws NetworkErrorException {
             AccountManager accountManager = getSystemService(AccountManager.class);
-            accountManager.addAccountExplicitly(new Account("foo", accountType), "bar", null);
+
+            String accountName = "foo" + sNumAccountsAdded;
+            sNumAccountsAdded++;
+
+            accountManager.addAccountExplicitly(new Account(accountName, accountType), "bar", null);
 
             Bundle result = new Bundle();
-            result.putString(AccountManager.KEY_ACCOUNT_NAME, "foo");
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
             result.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
             response.onResult(result);
 
diff --git a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider2.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider2.java
new file mode 100644
index 0000000..0ecb23b
--- /dev/null
+++ b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider2.java
@@ -0,0 +1,20 @@
+/*
+ * 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 com.android.cts.stub;
+
+public class StubProvider2 extends StubProvider {
+}
diff --git a/tests/tests/content/lib/accountaccess/Android.mk b/tests/tests/content/lib/accountaccess/Android.mk
index 559dc90..2832452 100644
--- a/tests/tests/content/lib/accountaccess/Android.mk
+++ b/tests/tests/content/lib/accountaccess/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_MODULE := accountaccesslib
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target \
+                               ub-uiautomator \
+                               compatibility-device-util
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/AlwaysSyncableSyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/AlwaysSyncableSyncService.java
new file mode 100644
index 0000000..80c496a
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/AlwaysSyncableSyncService.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.cts.content;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AlwaysSyncableSyncService extends Service {
+    private static final Object sLock = new Object();
+    private static SyncAdapter sInstance;
+
+    public static SyncAdapter getInstance(Context context) {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new SyncAdapter(context.getApplicationContext(), false);
+            }
+        }
+
+        return sInstance;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return getInstance(this).getSyncAdapterBinder();
+    }
+}
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
index 2608b97..2d41ad6 100644
--- a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
@@ -15,6 +15,8 @@
 
 package com.android.cts.content;
 
+import android.util.Log;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -25,6 +27,8 @@
  */
 // TODO: Move this puppy in a common place, so ppl can use it.
 public class FlakyTestRule implements TestRule {
+    private static final String LOG_TAG = FlakyTestRule.class.getSimpleName();
+
     private final int mAttemptCount;
 
     public FlakyTestRule(int attemptCount) {
@@ -42,6 +46,8 @@
                         statement.evaluate();
                         return;
                     } catch (Throwable t) {
+                        Log.e(LOG_TAG, "Test failed ", t);
+
                         throwable = t;
                     }
                 }
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/NotAlwaysSyncableSyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/NotAlwaysSyncableSyncService.java
new file mode 100644
index 0000000..58524cd
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/NotAlwaysSyncableSyncService.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.cts.content;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class NotAlwaysSyncableSyncService extends Service {
+    private static final Object sLock = new Object();
+    private static SyncAdapter sInstance;
+
+    public static SyncAdapter getInstance(Context context) {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new SyncAdapter(context.getApplicationContext(), false);
+            }
+        }
+
+        return sInstance;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return getInstance(this).getSyncAdapterBinder();
+    }
+}
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
index 19fb8ee..a81d6cc 100644
--- a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
@@ -27,14 +27,15 @@
 import android.os.Bundle;
 
 public class SyncAdapter extends AbstractThreadedSyncAdapter {
-    private static final Object sLock = new Object();
-    private static AbstractThreadedSyncAdapter sDelegate;
+    private final Object mLock = new Object();
+    private AbstractThreadedSyncAdapter mDelegate;
 
-    public static AbstractThreadedSyncAdapter setNewDelegate() {
+    public AbstractThreadedSyncAdapter setNewDelegate() {
         AbstractThreadedSyncAdapter delegate = mock(AbstractThreadedSyncAdapter.class);
+        when(delegate.onUnsyncableAccount()).thenCallRealMethod();
 
-        synchronized (sLock) {
-            sDelegate = delegate;
+        synchronized (mLock) {
+            mDelegate = delegate;
         }
 
         return delegate;
@@ -45,8 +46,8 @@
     }
 
     private AbstractThreadedSyncAdapter getCopyOfDelegate() {
-        synchronized (sLock) {
-            return sDelegate;
+        synchronized (mLock) {
+            return mDelegate;
         }
     }
 
@@ -59,4 +60,15 @@
             delegate.onPerformSync(account, extras, authority, provider, syncResult);
         }
     }
+
+    @Override
+    public boolean onUnsyncableAccount() {
+        AbstractThreadedSyncAdapter delegate = getCopyOfDelegate();
+
+        if (delegate == null) {
+            return true;
+        } else {
+            return delegate.onUnsyncableAccount();
+        }
+    }
 }
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
deleted file mode 100644
index e8da82c..0000000
--- a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.cts.content;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class SyncService extends Service {
-    private SyncAdapter mInstance;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mInstance = new SyncAdapter(this, false);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mInstance.getSyncAdapterBinder();
-    }
-}
diff --git a/tests/tests/content/lib/accountaccess/src/com.android.cts.content/Utils.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/Utils.java
new file mode 100644
index 0000000..b2ff8c9
--- /dev/null
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/Utils.java
@@ -0,0 +1,132 @@
+package com.android.cts.content;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncRequest;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+
+public class Utils {
+    private static final String LOG_TAG = Utils.class.getSimpleName();
+
+    public static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
+    public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
+    public static final String SYNC_ACCOUNT_TYPE = "com.stub";
+    public static final String ALWAYS_SYNCABLE_AUTHORITY = "com.android.cts.stub.provider";
+    public static final String NOT_ALWAYS_SYNCABLE_AUTHORITY = "com.android.cts.stub.provider2";
+
+    public static boolean hasDataConnection() {
+        ConnectivityManager connectivityManager = getContext().getSystemService(
+                ConnectivityManager.class);
+        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+    }
+
+    public static boolean hasNotificationSupport() {
+        return !getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    public static Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    public static boolean isWatch() {
+        return (getContext().getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH;
+    }
+
+    public static UiDevice getUiDevice() {
+        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    public static void waitForSyncManagerAccountChangeUpdate() {
+        // Wait for the sync manager to be notified for the new account.
+        // Unfortunately, there is no way to detect this event, sigh...
+        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
+    }
+
+    public static void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist +" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
+    }
+
+    public static  void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
+        // Allow us to run in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd deviceidle whitelist -" + getContext().getPackageName());
+        // Allow us to use data in the background
+        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
+    }
+
+    public static class ClosableAccount implements AutoCloseable {
+        public final Account account;
+
+        private ClosableAccount(@NonNull Account account) {
+            this.account = account;
+        }
+
+        @Override
+        public void close() throws Exception {
+            AccountManager accountManager = getContext().getSystemService(AccountManager.class);
+
+            accountManager.getAuthToken(account, TOKEN_TYPE_REMOVE_ACCOUNTS, null, false, null,
+                    null);
+        }
+    }
+
+    public static ClosableAccount withAccount(@NonNull Activity activity)
+            throws AuthenticatorException, OperationCanceledException, IOException {
+        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
+        Bundle result = accountManager.addAccount(SYNC_ACCOUNT_TYPE, null, null, null,
+                activity, null, null).getResult();
+        Account addedAccount = new Account(
+                result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+        Log.i(LOG_TAG, "Added account " + addedAccount);
+
+        waitForSyncManagerAccountChangeUpdate();
+
+        return new ClosableAccount(addedAccount);
+    }
+
+    public static SyncRequest requestSync(String authority) {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
+        extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+        SyncRequest request = new SyncRequest.Builder()
+                .setSyncAdapter(null, authority)
+                .syncOnce()
+                .setExtras(extras)
+                .setExpedited(true)
+                .setManual(true)
+                .build();
+        ContentResolver.requestSync(request);
+
+        return request;
+    }
+}
diff --git a/tests/tests/content/res/xml/accountaccesssyncadapter.xml b/tests/tests/content/res/xml/always_syncable_account_access_adapter.xml
similarity index 100%
rename from tests/tests/content/res/xml/accountaccesssyncadapter.xml
rename to tests/tests/content/res/xml/always_syncable_account_access_adapter.xml
diff --git a/tests/tests/content/res/xml/not_always_syncable_account_access_adapter.xml b/tests/tests/content/res/xml/not_always_syncable_account_access_adapter.xml
new file mode 100644
index 0000000..bc2e9fd
--- /dev/null
+++ b/tests/tests/content/res/xml/not_always_syncable_account_access_adapter.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<sync-adapter
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="com.android.cts.stub.provider2"
+    android:accountType="com.stub"
+    android:userVisible="false"
+    android:supportsUploading="false"
+    android:allowParallelSyncs="false"
+    android:isAlwaysSyncable="false">
+</sync-adapter>
diff --git a/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
index db27352..95af4ca 100644
--- a/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
+++ b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
@@ -16,34 +16,26 @@
 
 package android.content.cts;
 
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static com.android.cts.content.Utils.ALWAYS_SYNCABLE_AUTHORITY;
+import static com.android.cts.content.Utils.SYNC_TIMEOUT_MILLIS;
+import static com.android.cts.content.Utils.allowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.disallowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.hasDataConnection;
+import static com.android.cts.content.Utils.requestSync;
+import static com.android.cts.content.Utils.withAccount;
 
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
 import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncRequest;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.content.AlwaysSyncableSyncService;
 import com.android.cts.content.FlakyTestRule;
 import com.android.cts.content.StubActivity;
-import com.android.cts.content.SyncAdapter;
 
 import org.junit.After;
 import org.junit.Before;
@@ -52,18 +44,17 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
-
 /**
  * Tests whether a sync adapter can access accounts.
  */
 @RunWith(AndroidJUnit4.class)
 public class AccountAccessSameCertTest {
-    private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
-
     @Rule
     public final TestRule mFlakyTestTRule = new FlakyTestRule(3);
 
+    @Rule
+    public final ActivityTestRule<StubActivity> activity = new ActivityTestRule(StubActivity.class);
+
     @Before
     public void setUp() throws Exception {
         allowSyncAdapterRunInBackgroundAndDataInBackground();
@@ -77,83 +68,15 @@
     @Test
     public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
         assumeTrue(hasDataConnection());
-        assumeTrue(hasNotificationSupport());
 
-        Intent intent = new Intent(getContext(), StubActivity.class);
-        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+        try (AutoCloseable ignored = withAccount(activity.getActivity())) {
+            AbstractThreadedSyncAdapter adapter = AlwaysSyncableSyncService.getInstance(
+                    activity.getActivity()).setNewDelegate();
 
-        AccountManager accountManager = getContext().getSystemService(AccountManager.class);
-        Bundle result = accountManager.addAccount("com.stub", null, null, null, activity,
-                null, null).getResult();
-
-        Account addedAccount = new Account(
-                result.getString(AccountManager.KEY_ACCOUNT_NAME),
-                        result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
-        waitForSyncManagerAccountChangeUpdate();
-
-        try {
-            AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
-
-            Bundle extras = new Bundle();
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
-            extras.putBoolean(ContentResolver.SYNC_EXTRAS_PRIORITY, true);
-            extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
-            SyncRequest request = new SyncRequest.Builder()
-                    .setSyncAdapter(null, "com.android.cts.stub.provider")
-                    .syncOnce()
-                    .setExtras(extras)
-                    .setExpedited(true)
-                    .setManual(true)
-                    .build();
-            ContentResolver.requestSync(request);
+            requestSync(ALWAYS_SYNCABLE_AUTHORITY);
 
             verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
                     any());
-        } finally {
-            accountManager.removeAccount(addedAccount, activity, null, null);
-            activity.finish();
         }
     }
-
-    private Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    private void waitForSyncManagerAccountChangeUpdate() {
-        // Wait for the sync manager to be notified for the new account.
-        // Unfortunately, there is no way to detect this event, sigh...
-        SystemClock.sleep(SYNC_TIMEOUT_MILLIS);
-    }
-
-    private boolean hasDataConnection() {
-        ConnectivityManager connectivityManager = getContext().getSystemService(
-                ConnectivityManager.class);
-        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
-        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
-    }
-
-    private boolean hasNotificationSupport() {
-        return !getContext().getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    private void allowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist +" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy add restrict-background-whitelist " + Process.myUid());
-    }
-
-    private void disallowSyncAdapterRunInBackgroundAndDataInBackground() throws IOException {
-        // Allow us to run in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd deviceidle whitelist -" + getContext().getPackageName());
-        // Allow us to use data in the background
-        SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                "cmd netpolicy remove restrict-background-whitelist " + Process.myUid());
-    }
 }
diff --git a/tests/tests/content/src/android/content/cts/DeferSyncTest.java b/tests/tests/content/src/android/content/cts/DeferSyncTest.java
new file mode 100644
index 0000000..457da10
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/DeferSyncTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.content.cts;
+
+import static com.android.cts.content.Utils.ALWAYS_SYNCABLE_AUTHORITY;
+import static com.android.cts.content.Utils.NOT_ALWAYS_SYNCABLE_AUTHORITY;
+import static com.android.cts.content.Utils.SYNC_TIMEOUT_MILLIS;
+import static com.android.cts.content.Utils.allowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.disallowSyncAdapterRunInBackgroundAndDataInBackground;
+import static com.android.cts.content.Utils.hasDataConnection;
+import static com.android.cts.content.Utils.requestSync;
+import static com.android.cts.content.Utils.withAccount;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentCaptor.forClass;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.cts.content.AlwaysSyncableSyncService;
+import com.android.cts.content.FlakyTestRule;
+import com.android.cts.content.NotAlwaysSyncableSyncService;
+import com.android.cts.content.StubActivity;
+import com.android.cts.content.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWith(AndroidJUnit4.class)
+public class DeferSyncTest {
+    @Rule
+    public final TestRule flakyTestRule = new FlakyTestRule(3);
+
+    @Rule
+    public final ActivityTestRule<StubActivity> activity = new ActivityTestRule(StubActivity.class);
+
+    @Before
+    public void setUp() throws Exception {
+        allowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        disallowSyncAdapterRunInBackgroundAndDataInBackground();
+    }
+
+    @Test
+    public void noSyncsWhenDeferred() throws Exception {
+        assumeTrue(hasDataConnection());
+
+        AbstractThreadedSyncAdapter notAlwaysSyncableAdapter =
+                NotAlwaysSyncableSyncService.getInstance(activity.getActivity()).setNewDelegate();
+        AbstractThreadedSyncAdapter alwaysSyncableAdapter =
+                AlwaysSyncableSyncService.getInstance(activity.getActivity()).setNewDelegate();
+
+        when(alwaysSyncableAdapter.onUnsyncableAccount()).thenReturn(false);
+        when(notAlwaysSyncableAdapter.onUnsyncableAccount()).thenReturn(false);
+
+        try (Utils.ClosableAccount ignored = withAccount(activity.getActivity())) {
+            requestSync(NOT_ALWAYS_SYNCABLE_AUTHORITY);
+            requestSync(ALWAYS_SYNCABLE_AUTHORITY);
+
+            Thread.sleep(SYNC_TIMEOUT_MILLIS);
+
+            verify(notAlwaysSyncableAdapter, atLeast(1)).onUnsyncableAccount();
+            verify(notAlwaysSyncableAdapter, never()).onPerformSync(any(), any(), any(), any(),
+                    any());
+
+            verify(alwaysSyncableAdapter, atLeast(1)).onUnsyncableAccount();
+            verify(alwaysSyncableAdapter, never()).onPerformSync(any(), any(), any(), any(), any());
+        }
+    }
+
+    @Test
+    public void deferSyncAndMakeSyncable() throws Exception {
+        assumeTrue(hasDataConnection());
+
+        AbstractThreadedSyncAdapter adapter = NotAlwaysSyncableSyncService.getInstance(
+                activity.getActivity()).setNewDelegate();
+        when(adapter.onUnsyncableAccount()).thenReturn(false);
+
+        try (Utils.ClosableAccount account = withAccount(activity.getActivity())) {
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onUnsyncableAccount();
+
+            // Enable the adapter by making the account/provider syncable
+            ContentResolver.setIsSyncable(account.account, NOT_ALWAYS_SYNCABLE_AUTHORITY, 1);
+            requestSync(NOT_ALWAYS_SYNCABLE_AUTHORITY);
+
+            ArgumentCaptor<Bundle> extrasCaptor = forClass(Bundle.class);
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(),
+                    extrasCaptor.capture(), any(), any(), any());
+
+            // As the adapter is made syncable, we should not get an initialization sync
+            assertFalse(
+                    extrasCaptor.getValue().containsKey(ContentResolver.SYNC_EXTRAS_INITIALIZE));
+        }
+    }
+
+    @Test
+    public void deferSyncAndReportIsReady() throws Exception {
+        assumeTrue(hasDataConnection());
+
+        AbstractThreadedSyncAdapter adapter = NotAlwaysSyncableSyncService.getInstance(
+                activity.getActivity()).setNewDelegate();
+        when(adapter.onUnsyncableAccount()).thenReturn(false);
+
+        try (Utils.ClosableAccount ignored = withAccount(activity.getActivity())) {
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onUnsyncableAccount();
+
+            // Enable the adapter by returning true from onNewAccount
+            when(adapter.onUnsyncableAccount()).thenReturn(true);
+            requestSync(NOT_ALWAYS_SYNCABLE_AUTHORITY);
+            verify(adapter, atLeast(1)).onUnsyncableAccount();
+
+            ArgumentCaptor<Bundle> extrasCaptor = forClass(Bundle.class);
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(),
+                    extrasCaptor.capture(), any(), any(), any());
+
+            // As the adapter is not syncable yet, we should get an initialization sync
+            assertTrue(extrasCaptor.getValue().getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE));
+        }
+    }
+
+    @Test
+    public void deferSyncAndReportIsReadyAlwaysSyncable() throws Exception {
+        assumeTrue(hasDataConnection());
+
+        AbstractThreadedSyncAdapter adapter = AlwaysSyncableSyncService.getInstance(
+                activity.getActivity()).setNewDelegate();
+        when(adapter.onUnsyncableAccount()).thenReturn(false);
+
+        try (Utils.ClosableAccount ignored = withAccount(activity.getActivity())) {
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onUnsyncableAccount();
+
+            // Enable the adapter by returning true from onNewAccount
+            when(adapter.onUnsyncableAccount()).thenReturn(true);
+            requestSync(ALWAYS_SYNCABLE_AUTHORITY);
+            verify(adapter, atLeast(1)).onUnsyncableAccount();
+
+            ArgumentCaptor<Bundle> extrasCaptor = forClass(Bundle.class);
+            verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(),
+                    extrasCaptor.capture(), any(), any(), any());
+
+            // The adapter is always syncable, hence there is no init sync
+            assertFalse(
+                    extrasCaptor.getValue().containsKey(ContentResolver.SYNC_EXTRAS_INITIALIZE));
+        }
+    }
+
+    @Test
+    public void onNewAccountForEachAccount() throws Exception {
+        assumeTrue(hasDataConnection());
+
+        AbstractThreadedSyncAdapter adapter = NotAlwaysSyncableSyncService.getInstance(
+                activity.getActivity()).setNewDelegate();
+        when(adapter.onUnsyncableAccount()).thenReturn(true, false);
+
+        try (Utils.ClosableAccount account1 = withAccount(activity.getActivity())) {
+            try (Utils.ClosableAccount account2 = withAccount(activity.getActivity())) {
+                verify(adapter, timeout(SYNC_TIMEOUT_MILLIS).atLeast(2)).onUnsyncableAccount();
+
+                // Exactly account should have gotten the init-sync. No further syncs happen as
+                // onNewAccount returns false again.
+                ArgumentCaptor<Bundle> extrasCaptor = forClass(Bundle.class);
+                verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(),
+                        extrasCaptor.capture(), any(), any(), any());
+                assertTrue(
+                        extrasCaptor.getValue().getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE));
+            }
+        }
+    }
+
+}
diff --git a/tests/tests/content/src/android/content/cts/MockSyncAdapter.java b/tests/tests/content/src/android/content/cts/MockSyncAdapter.java
index 957859e..c8b0bec 100644
--- a/tests/tests/content/src/android/content/cts/MockSyncAdapter.java
+++ b/tests/tests/content/src/android/content/cts/MockSyncAdapter.java
@@ -19,6 +19,7 @@
 import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ISyncAdapter;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
 import android.content.ISyncContext;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -76,6 +77,12 @@
         this.mLatch = mLatch;
     }
 
+    @Override
+    public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb)
+            throws RemoteException {
+        cb.onUnsyncableAccountDone(true);
+    }
+
     public void startSync(ISyncContext syncContext, String authority, Account account,
             Bundle extras) throws RemoteException {
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index 13cc2e0..3c26f39 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -213,6 +213,39 @@
         }
     }
 
+    private Boolean isClosed = new Boolean(false);
+
+    @Test
+    public void testCreateFromStream2() throws IOException {
+        FileInputStream inputStream = null;
+        File imageFile = null;
+        synchronized (isClosed) {
+            isClosed = false;
+            try {
+                imageFile = new File(mContext.getFilesDir(), "tempimage.jpg");
+                writeSampleImage(imageFile);
+
+                inputStream = new FileInputStream(imageFile) {
+                    @Override
+                    public void close() throws IOException {
+                        super.close();
+                        isClosed = true;
+                    }
+                };
+                assertNotNull(Drawable.createFromStream(inputStream, "Sample"));
+            } finally {
+                if (null != inputStream) {
+		    // verify that the stream was not closed
+                    assertFalse(isClosed);
+                    inputStream.close();
+                }
+                if (imageFile.exists()) {
+                    assertTrue(imageFile.delete());
+                }
+            }
+        }
+    }
+
     @Test
     public void testCreateFromResourceStream1() throws IOException {
         FileInputStream inputEmptyStream = null;
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
index 9439ed1..9c4d6f2 100644
--- a/tests/tests/media/libmediandkjni/native-media-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -35,6 +35,7 @@
 #include "media/NdkMediaExtractor.h"
 #include "media/NdkMediaCodec.h"
 #include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDataSource.h"
 #include "media/NdkMediaFormat.h"
 #include "media/NdkMediaMuxer.h"
 
@@ -76,6 +77,49 @@
     }
 };
 
+struct FdDataSource {
+
+    FdDataSource(int fd, jlong offset, jlong size)
+        : mFd(fd),
+          mOffset(offset),
+          mSize(size) {
+    }
+
+    ssize_t readAt(off64_t offset, void *data, size_t size) {
+        ssize_t ssize = size;
+        if (!data || offset < 0 || offset >= mSize || offset + ssize < offset) {
+            return -1;
+        }
+        if (offset + ssize > mSize) {
+            ssize = mSize - offset;
+        }
+        if (lseek(mFd, mOffset + offset, SEEK_SET) < 0) {
+            return -1;
+        }
+        return read(mFd, data, ssize);
+    }
+
+    ssize_t getSize() {
+        return mSize;
+    }
+
+private:
+
+    int mFd;
+    off64_t mOffset;
+    int64_t mSize;
+
+};
+
+static ssize_t FdSourceReadAt(void *userdata, off64_t offset, void *data, size_t size) {
+    FdDataSource *src = (FdDataSource*) userdata;
+    return src->readAt(offset, data, size);
+}
+
+static ssize_t FdSourceGetSize(void *userdata) {
+    FdDataSource *src = (FdDataSource*) userdata;
+    return src->getSize();
+}
 
 
 jobject testExtractor(AMediaExtractor *ex, JNIEnv *env) {
@@ -220,11 +264,22 @@
 }
 
 extern "C" jobject Java_android_media_cts_NativeDecoderTest_getDecodedDataNative(JNIEnv *env,
-        jclass /*clazz*/, int fd, jlong offset, jlong size) {
+        jclass /*clazz*/, int fd, jlong offset, jlong size, jboolean wrapFd) {
     ALOGV("getDecodedDataNative");
 
+    FdDataSource fdSrc(fd, offset, size);
     AMediaExtractor *ex = AMediaExtractor_new();
-    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    AMediaDataSource *ndkSrc = AMediaDataSource_new();
+
+    int err;
+    if (wrapFd) {
+        AMediaDataSource_setUserdata(ndkSrc, &fdSrc);
+        AMediaDataSource_setReadAt(ndkSrc, FdSourceReadAt);
+        AMediaDataSource_setGetSize(ndkSrc, FdSourceGetSize);
+        err = AMediaExtractor_setDataSourceCustom(ex, ndkSrc);
+    } else {
+        err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    }
     if (err != 0) {
         ALOGE("setDataSource error: %d", err);
         return NULL;
@@ -367,6 +422,7 @@
     delete[] format;
     delete[] codec;
     AMediaExtractor_delete(ex);
+    AMediaDataSource_delete(ndkSrc);
     return ret;
 }
 
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index 32cf661..f688e09 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -203,7 +203,18 @@
         }
     }
 
+    public void testDataSource() throws Exception {
+        int testsRun = testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz, true);
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
     private int testDecoder(int res) throws Exception {
+        return testDecoder(res, /* wrapFd */ false);
+    }
+
+    private int testDecoder(int res, boolean wrapFd) throws Exception {
         if (!MediaUtils.hasCodecsForResource(mContext, res)) {
             return 0; // skip
         }
@@ -213,7 +224,7 @@
         int[] jdata = getDecodedData(
                 fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
         int[] ndata = getDecodedDataNative(
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(), wrapFd);
 
         fd.close();
         Log.i("@@@", Arrays.toString(jdata));
@@ -384,7 +395,7 @@
         return ret;
     }
 
-    private static native int[] getDecodedDataNative(int fd, long offset, long size)
+    private static native int[] getDecodedDataNative(int fd, long offset, long size, boolean wrapFd)
             throws IOException;
 
     public void testVideoPlayback() throws Exception {