RESTRICT AUTOMERGE Fix off-by-1 in frame checksum calculation
am: 91aa5a5c1a

Change-Id: I736e3a3990dfdff0e236623cdff3fcca1ca8e4ec
diff --git a/apps/CameraITS/tests/scene1/test_dng_noise_model.py b/apps/CameraITS/tests/scene1/test_dng_noise_model.py
index ba8fd7d..98efd4e 100644
--- a/apps/CameraITS/tests/scene1/test_dng_noise_model.py
+++ b/apps/CameraITS/tests/scene1/test_dng_noise_model.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import math
 import os.path
 import its.caps
 import its.device
@@ -25,7 +26,7 @@
 DIFF_THRESH = 0.0012  # absolute variance delta threshold
 FRAC_THRESH = 0.2  # relative variance delta threshold
 NUM_STEPS = 4
-STATS_GRID = 49  # center 2.04% of image for calculations
+SENS_TOL = 0.97  # specification is <= 3%
 
 
 def main():
@@ -41,51 +42,42 @@
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         props = cam.override_with_hidden_physical_camera_props(props)
-        its.caps.skip_unless(its.caps.raw(props) and
+        its.caps.skip_unless(
+                its.caps.raw(props) and
                 its.caps.raw16(props) and
                 its.caps.manual_sensor(props) and
                 its.caps.read_3a(props) and
                 its.caps.per_frame_control(props) and
                 not its.caps.mono_camera(props))
 
-        debug = its.caps.debug_mode()
-
         white_level = float(props['android.sensor.info.whiteLevel'])
         cfa_idxs = its.image.get_canonical_cfa_order(props)
-        aax = props['android.sensor.info.preCorrectionActiveArraySize']['left']
-        aay = props['android.sensor.info.preCorrectionActiveArraySize']['top']
-        aaw = props['android.sensor.info.preCorrectionActiveArraySize']['right']-aax
-        aah = props['android.sensor.info.preCorrectionActiveArraySize']['bottom']-aay
 
         # Expose for the scene with min sensitivity
-        sens_min, sens_max = props['android.sensor.info.sensitivityRange']
-        sens_step = (sens_max - sens_min) / NUM_STEPS
+        sens_min, _ = props['android.sensor.info.sensitivityRange']
+        sens_max_ana = props['android.sensor.maxAnalogSensitivity']
+        sens_step = (sens_max_ana - sens_min) / NUM_STEPS
         s_ae, e_ae, _, _, f_dist = cam.do_3a(get_results=True)
         s_e_prod = s_ae * e_ae
-        sensitivities = range(sens_min, sens_max, sens_step)
+        sensitivities = range(sens_min, sens_max_ana+1, sens_step)
 
         var_expected = [[], [], [], []]
         var_measured = [[], [], [], []]
-        x = STATS_GRID/2  # center in H of STATS_GRID
-        y = STATS_GRID/2  # center in W of STATS_GRID
+        sens_valid = []
         for sens in sensitivities:
-
             # Capture a raw frame with the desired sensitivity
             exp = int(s_e_prod / float(sens))
             req = its.objects.manual_capture_request(sens, exp, f_dist)
-            if debug:
-                cap = cam.do_capture(req, cam.CAP_RAW)
-                planes = its.image.convert_capture_to_planes(cap, props)
-            else:
-                cap = cam.do_capture(req, {'format': 'rawStats',
-                                           'gridWidth': aaw/STATS_GRID,
-                                           'gridHeight': aah/STATS_GRID})
-                mean_img, var_img = its.image.unpack_rawstats_capture(cap)
+            cap = cam.do_capture(req, cam.CAP_RAW)
+            planes = its.image.convert_capture_to_planes(cap, props)
+            s_read = cap['metadata']['android.sensor.sensitivity']
+            print 'iso_write: %d, iso_read: %d' % (sens, s_read)
 
             # Test each raw color channel (R, GR, GB, B)
             noise_profile = cap['metadata']['android.sensor.noiseProfile']
             assert len(noise_profile) == len(BAYER_LIST)
             for i in range(len(BAYER_LIST)):
+                print BAYER_LIST[i],
                 # Get the noise model parameters for this channel of this shot.
                 ch = cfa_idxs[i]
                 s, o = noise_profile[ch]
@@ -96,23 +88,43 @@
                 black_level = its.image.get_black_level(i, props,
                                                         cap['metadata'])
                 level_range = white_level - black_level
-                if debug:
-                    plane = ((planes[i] * white_level - black_level) /
-                             level_range)
-                    tile = its.image.get_image_patch(plane, 0.49, 0.49,
-                                                     0.02, 0.02)
-                    mean_img_ch = tile.mean()
-                    var_measured[i].append(
-                            its.image.compute_image_variances(tile)[0])
-                else:
-                    mean_img_ch = (mean_img[x, y, ch]-black_level)/level_range
-                    var_measured[i].append(var_img[x, y, ch]/level_range**2)
-                var_expected[i].append(s * mean_img_ch + o)
+                plane = its.image.get_image_patch(planes[i], 0.49, 0.49,
+                                                  0.02, 0.02)
+                tile_raw = plane * white_level
+                tile_norm = ((tile_raw - black_level) / level_range)
 
+                # exit if distribution is clipped at 0, otherwise continue
+                mean_img_ch = tile_norm.mean()
+                var_model = s * mean_img_ch + o
+                # This computation is a suspicious because if the data were
+                # clipped, the mean and standard deviation could be affected
+                # in a way that affects this check. However, empirically,
+                # the mean and standard deviation change more slowly than the
+                # clipping point itself does, so the check remains correct
+                # even after the signal starts to clip.
+                mean_minus_3sigma = mean_img_ch - math.sqrt(var_model) * 3
+                if mean_minus_3sigma < 0:
+                    e_msg = '\nPixel distribution crosses 0.\n'
+                    e_msg += 'Likely black level over-clips.\n'
+                    e_msg += 'Linear model is not valid.\n'
+                    e_msg += 'mean: %.3e, var: %.3e, u-3s: %.3e' % (
+                            mean_img_ch, var_model, mean_minus_3sigma)
+                    assert 0, e_msg
+                else:
+                    print 'mean:', mean_img_ch,
+                    var_measured[i].append(
+                            its.image.compute_image_variances(tile_norm)[0])
+                    print 'var:', var_measured[i][-1],
+                    var_expected[i].append(var_model)
+                    print 'var_model:', var_expected[i][-1]
+            print ''
+            sens_valid.append(sens)
+
+    # plot data and models
     for i, ch in enumerate(BAYER_LIST):
-        pylab.plot(sensitivities, var_expected[i], 'rgkb'[i],
+        pylab.plot(sens_valid, var_expected[i], 'rgkb'[i],
                    label=ch+' expected')
-        pylab.plot(sensitivities, var_measured[i], 'rgkb'[i]+'--',
+        pylab.plot(sens_valid, var_measured[i], 'rgkb'[i]+'.--',
                    label=ch+' measured')
     pylab.xlabel('Sensitivity')
     pylab.ylabel('Center patch variance')
@@ -122,7 +134,7 @@
     # PASS/FAIL check
     for i, ch in enumerate(BAYER_LIST):
         diffs = [abs(var_measured[i][j] - var_expected[i][j])
-                 for j in range(len(sensitivities))]
+                 for j in range(len(sens_valid))]
         print 'Diffs (%s):'%(ch), diffs
         for j, diff in enumerate(diffs):
             thresh = max(DIFF_THRESH, FRAC_THRESH*var_expected[i][j])
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
index a65c630..a5f85ca 100644
--- a/apps/CameraITS/tests/scene1/test_param_shading_mode.py
+++ b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
@@ -58,6 +58,13 @@
         #      switching shading modes.
         cam.do_3a(mono_camera=mono_camera)
 
+        # Use smallest yuv size matching the aspect ratio of largest yuv size to
+        # reduce some USB bandwidth overhead since we are only looking at output
+        # metadata in this test.
+        largest_yuv_fmt = its.objects.get_largest_yuv_format(props)
+        largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height'])
+        cap_fmt = its.objects.get_smallest_yuv_format(props, largest_yuv_size)
+
         # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY
         # in different sessions.
         # reference_maps[mode]
@@ -68,7 +75,7 @@
             req = its.objects.auto_capture_request()
             req['android.statistics.lensShadingMapMode'] = 1
             req['android.shading.mode'] = mode
-            cap_res = cam.do_capture([req]*NUM_FRAMES)[NUM_FRAMES-1]['metadata']
+            cap_res = cam.do_capture([req]*NUM_FRAMES, cap_fmt)[NUM_FRAMES-1]['metadata']
             lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
             assert(lsc_map.has_key('width') and
                    lsc_map.has_key('height') and
@@ -89,7 +96,7 @@
                     req['android.shading.mode'] = mode
                     reqs.append(req)
 
-        caps = cam.do_capture(reqs)
+        caps = cam.do_capture(reqs, cap_fmt)
 
         # shading_maps[mode][loop]
         shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)]
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1962b50..1c7b4aa 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2895,6 +2895,13 @@
             <meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
         </activity>
 
+        <receiver
+            android:name=".managedprovisioning.ByodFlowTestActivity$ProvisioningCompleteReceiver">
+            <intent-filter>
+                <action android:name="android.app.action.MANAGED_PROFILE_PROVISIONED" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".managedprovisioning.CompTestActivity"
                 android:launchMode="singleTask"
                 android:label="@string/comp_test">
diff --git a/apps/CtsVerifier/res/layout/pro_audio.xml b/apps/CtsVerifier/res/layout/pro_audio.xml
index 71edb71..3182499 100644
--- a/apps/CtsVerifier/res/layout/pro_audio.xml
+++ b/apps/CtsVerifier/res/layout/pro_audio.xml
@@ -13,6 +13,24 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <TextView
+            android:text="@string/proAudioHasProAudiolbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHasProAudioLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
             android:text="@string/proAudioHasLLAlbl"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 057ac46..8a6c1d7 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2803,7 +2803,7 @@
         Please press the Go button to open the Settings page.
         (If this device has a separate app for work settings, ignore the Go button and navigate to that app manually).\n
         \n
-        Navigate to \"Data usage\" page and then into the \"Wi-Fi data usage\" category.\n
+        Navigate to the \"Network &amp; Internet\" page and then click on \"Wi-Fi\" and then \"Wi-Fi data usage\".\n
         Confirm that \"All work apps\" section is present and that it is possible to see the data usage for work (badged) apps.\n
         (If the section is not present, this might be because work apps have not used Wi-Fi data yet. Ensure that you have used Wi-Fi data on a work app, then repeat these instructions.)\n
         \n
@@ -2819,7 +2819,7 @@
         Please press the Go button to open the Settings page.
         (If this device has a separate app for work settings, ignore the Go button and navigate to that app manually).\n
         \n
-        Navigate to \"Data usage\" page and then into the \"Mobile data usage\" category.\n
+        Navigate to the \"Network &amp; Internet\" page and then click on \"Mobile network\" and then \"App data usage\".\n
         Confirm that \"All work apps\" section is present and that it is possible to see the data usage for work (badged) apps.\n
         \n
         Then use the Back button (or navigate back to this app using Recents) to return to this test and mark accordingly.
@@ -3639,10 +3639,12 @@
     <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 \'Multiple users\' setting. \n\n
+        Then press \'Go\' to open \'Multiple users\' setting. \n
+        Click the Settings icon adjacent to the managed user.\n\n
 
         Mark this test as passed if:\n\n
-        - Main switch is disabled and in off position\n
+        - \"Delete User\" is disabled.\n
+        - Tapping \"Delete user\" shows an \"Action not allowed\" dialog.\n
         \n
         Use the Back button to return to this page.
     </string>
@@ -3650,8 +3652,9 @@
         Please press the \'Set restriction\' button to set the user restriction.
         Then press \'Go\' to open \'Multiple users\' setting. \n\n
 
-        Mark this test as passed if one of the following conditions is met:\n\n
-        - Main switch is disabled and in off position\n
+        Mark this test as passed if all of the following conditions are met:\n\n
+        - The \"Delete managed user from this device\" option in the overflow menu is disabled\n
+        - Clicking \"Delete managed user from this device\" in the overflow menu results in an \"Action not allowed\" dialog.\n
         \n
         Use the Back button to return to this page.
     </string>
@@ -3962,7 +3965,7 @@
         7) Verify that at the bottom of Quick Settings, you are told the device is managed by \"Foo, Inc.\".\n
         8) Tap on the information.\n
         9) Verify that a dialog informing you about device monitoring opens.\n
-        10) Tap the \"Learn more\" link.\n
+        10) Tap the \"View Policies\" button.\n
         11) Verify that a screen informing you what your managing organization can do is shown.\n
         \n
         Use the Back button to return to this page. If this device does not have quick settings, please skip this test and mark it passing.
@@ -4424,6 +4427,7 @@
     <!--  Pro Audio Tests -->
     <string name="pro_audio_latency_test">Pro Audio Test</string>
 
+    <string name="proAudioHasProAudiolbl">Has Pro Audio</string>
     <string name="proAudioHasLLAlbl">Has Low-Latency Audio</string>
     <string name="proAudioInputLbl">Audio Input:</string>
     <string name="proAudioOutputLbl">Audio Output:</string>
@@ -4444,6 +4448,10 @@
     <string name="audio_proaudio_NA">N/A</string>
     <string name="audio_proaudio_pending">pending...</string>
 
+    <string name="audio_proaudio_nopa_title">Pro Audio Test</string>
+    <string name="audio_proaudio_nopa_message">This device does not set the FEATURE_AUDIO_PRO
+        flag and therefore does not need to run this test.</string>
+
     <!--  MIDI Test -->
     <string name="midi_test">MIDI Test</string>
     <string name="ndk_midi_test">Native MIDI API Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index 30bc5fc..07a2d4d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -46,6 +46,7 @@
     private static final boolean DEBUG = false;
 
     // Flags
+    private boolean mClaimsProAudio;
     private boolean mClaimsLowLatencyAudio;    // CDD ProAudio section C-1-1
     private boolean mClaimsMIDI;               // CDD ProAudio section C-1-4
     private boolean mClaimsUSBHostMode;        // CDD ProAudio section C-1-3
@@ -75,10 +76,19 @@
 
     CheckBox mClaimsHDMICheckBox;
 
+    // Borrowed from PassFailButtons.java
+    private static final int INFO_DIALOG_ID = 1337;
+    private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId";
+    private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
+
     public ProAudioActivity() {
         super();
     }
 
+    private boolean claimsProAudio() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);
+    }
+
     private boolean claimsLowLatencyAudio() {
         // CDD Section C-1-1: android.hardware.audio.low_latency
         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
@@ -158,13 +168,13 @@
     }
 
     private void calculatePass() {
-        boolean hasPassed =
-                mClaimsLowLatencyAudio && mClaimsMIDI &&
+        boolean hasPassed = !mClaimsProAudio ||
+                (mClaimsLowLatencyAudio && mClaimsMIDI &&
                 mClaimsUSBHostMode && mClaimsUSBPeripheralMode &&
                 (!mClaimsHDMI || isHDMIValid()) &&
                 mOutputDevInfo != null && mInputDevInfo != null &&
                 mRoundTripLatency != 0.0 && mRoundTripLatency <= LATENCY_MS_LIMIT &&
-                mRoundTripConfidence >= CONFIDENCE_LIMIT;
+                mRoundTripConfidence >= CONFIDENCE_LIMIT);
         getPassButton().setEnabled(hasPassed);
     }
 
@@ -261,6 +271,16 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.proaudio_test, R.string.proaudio_info, -1);
 
+        mClaimsProAudio = claimsProAudio();
+        ((TextView)findViewById(R.id.proAudioHasProAudioLbl)).setText("" + mClaimsProAudio);
+
+        if (!mClaimsProAudio) {
+            Bundle args = new Bundle();
+            args.putInt(INFO_DIALOG_TITLE_ID, R.string.pro_audio_latency_test);
+            args.putInt(INFO_DIALOG_MESSAGE_ID, R.string.audio_proaudio_nopa_message);
+            showDialog(INFO_DIALOG_ID, args);
+        }
+
         mClaimsLowLatencyAudio = claimsLowLatencyAudio();
         ((TextView)findViewById(R.id.proAudioHasLLALbl)).setText("" + mClaimsLowLatencyAudio);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index e781558..cef8ae3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -19,12 +19,17 @@
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
@@ -59,6 +64,7 @@
     protected static final String HELPER_APP_PATH = "/data/local/tmp/NotificationBot.apk";
 
     private static final String TAG = "ByodFlowTestActivity";
+    private static final int PROVISIONING_CHECK_PERIOD_MS = 3000;
     private static ConnectivityManager mCm;
     private static final int REQUEST_MANAGED_PROVISIONING = 0;
     private static final int REQUEST_PROFILE_OWNER_STATUS = 1;
@@ -66,6 +72,13 @@
     private static final int REQUEST_CHECK_DISK_ENCRYPTION = 3;
     private static final int REQUEST_SET_LOCK_FOR_ENCRYPTION = 4;
 
+    private static final String PROVISIONING_PREFERENCES = "provisioning_preferences";
+    private static final String PREFERENCE_PROVISIONING_COMPLETE_STATUS =
+            "provisioning_complete_status";
+    private static final int PREFERENCE_PROVISIONING_COMPLETE_STATUS_NOT_RECEIVED = 0;
+    private static final int PREFERENCE_PROVISIONING_COMPLETE_STATUS_RECEIVED = 1;
+    private static final int PREFERENCE_PROVISIONING_COMPLETE_STATUS_PROCESSED = 2;
+
     private ComponentName mAdminReceiverComponent;
     private KeyguardManager mKeyguardManager;
     private ByodFlowTestHelper mByodFlowTestHelper;
@@ -116,6 +129,26 @@
     private TestListItem mPolicyTransparencyTest;
     private TestListItem mTurnOffWorkFeaturesTest;
     private TestListItem mWidgetTest;
+    private final Handler mHandler = new Handler(Looper.myLooper());
+
+    private final Runnable mPeriodicProvisioningCheckRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (isProvisioningCompleteBroadcastReceived(getApplicationContext())) {
+                markProvisioningCompleteBroadcastProcessed(getApplicationContext());
+                queryProfileOwner(true);
+            } else {
+                mHandler.postDelayed(this, PROVISIONING_CHECK_PERIOD_MS);
+            }
+        }
+    };
+
+    public static class ProvisioningCompleteReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            markProvisioningCompleteBroadcastReceived(context);
+        }
+    }
 
     public ByodFlowTestActivity() {
         super(R.layout.provisioning_byod,
@@ -152,6 +185,27 @@
     }
 
     @Override
+    protected void onStart() {
+        super.onStart();
+        startPeriodicProvisioningCheckIfNecessary();
+    }
+
+    private void startPeriodicProvisioningCheckIfNecessary() {
+        if (mHandler.hasCallbacks(mPeriodicProvisioningCheckRunnable)) {
+            return;
+        }
+        if (!isProvisioningCompleteBroadcastProcessed(this)) {
+            mHandler.post(mPeriodicProvisioningCheckRunnable);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mHandler.removeCallbacks(mPeriodicProvisioningCheckRunnable);
+    }
+
+    @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         if (ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS.equals(intent.getAction())) {
@@ -192,7 +246,7 @@
     private void handleStatusUpdate(int resultCode, Intent data) {
         boolean provisioned = data != null &&
                 data.getBooleanExtra(ByodHelperActivity.EXTRA_PROVISIONED, false);
-        setTestResult(mProfileOwnerInstalled, (provisioned && resultCode == RESULT_OK) ?
+        setProfileOwnerTestResult((provisioned && resultCode == RESULT_OK) ?
                 TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
     }
 
@@ -698,13 +752,21 @@
         }
         catch (ActivityNotFoundException e) {
             Log.d(TAG, "queryProfileOwner: ActivityNotFoundException", e);
-            setTestResult(mProfileOwnerInstalled, TestResult.TEST_RESULT_FAILED);
+            setProfileOwnerTestResult(TestResult.TEST_RESULT_FAILED);
             if (showToast) {
                 Utils.showToast(this, R.string.provisioning_byod_no_activity);
             }
         }
     }
 
+    private void setProfileOwnerTestResult(int result) {
+        setTestResult(mProfileOwnerInstalled, result);
+        if (result == TestResult.TEST_RESULT_FAILED) {
+            clearProvisioningCompleteBroadcastStatus(this);
+            startPeriodicProvisioningCheckIfNecessary();
+        }
+    }
+
     private void checkDiskEncryption() {
         try {
             Intent intent = new Intent(ByodHelperActivity.ACTION_CHECK_DISK_ENCRYPTION);
@@ -798,4 +860,42 @@
             new ComponentName(ByodFlowTestActivity.this, HandleIntentActivity.class.getName()),
             enableState, PackageManager.DONT_KILL_APP);
     }
+
+    private static void markProvisioningCompleteBroadcastReceived(Context context) {
+        markProvisioningCompleteBroadcastWithStatus(context,
+                PREFERENCE_PROVISIONING_COMPLETE_STATUS_RECEIVED);
+    }
+
+    private static void markProvisioningCompleteBroadcastProcessed(Context context) {
+        markProvisioningCompleteBroadcastWithStatus(context,
+                PREFERENCE_PROVISIONING_COMPLETE_STATUS_PROCESSED);
+    }
+
+    private static void clearProvisioningCompleteBroadcastStatus(Context context) {
+        markProvisioningCompleteBroadcastWithStatus(context,
+                PREFERENCE_PROVISIONING_COMPLETE_STATUS_NOT_RECEIVED);
+    }
+
+    private static void markProvisioningCompleteBroadcastWithStatus(Context context, int status) {
+        final SharedPreferences prefs = getProvisioningPreferences(context);
+        final SharedPreferences.Editor editor = prefs.edit();
+        editor.putInt(PREFERENCE_PROVISIONING_COMPLETE_STATUS, status);
+        editor.commit();
+    }
+
+    private static boolean isProvisioningCompleteBroadcastReceived(Context context) {
+        return getProvisioningPreferences(context)
+                .getInt(PREFERENCE_PROVISIONING_COMPLETE_STATUS, 0) ==
+                PREFERENCE_PROVISIONING_COMPLETE_STATUS_RECEIVED;
+    }
+
+    private static boolean isProvisioningCompleteBroadcastProcessed(Context context) {
+        return getProvisioningPreferences(context)
+                .getInt(PREFERENCE_PROVISIONING_COMPLETE_STATUS, 0) ==
+                PREFERENCE_PROVISIONING_COMPLETE_STATUS_PROCESSED;
+    }
+
+    private static SharedPreferences getProvisioningPreferences(Context context) {
+        return context.getSharedPreferences(PROVISIONING_PREFERENCES, MODE_PRIVATE);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index e59e6d6..66ff174 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -64,8 +64,6 @@
     public static final String ACTION_PROFILE_OWNER_STATUS = "com.android.cts.verifier.managedprovisioning.BYOD_STATUS";
     // Primary -> managed intent: request to delete the current profile
     public static final String ACTION_REMOVE_MANAGED_PROFILE = "com.android.cts.verifier.managedprovisioning.BYOD_REMOVE";
-    // Managed -> managed intent: provisioning completed successfully
-    public static final String ACTION_PROFILE_PROVISIONED = "com.android.cts.verifier.managedprovisioning.BYOD_PROVISIONED";
     // Primary -> managed intent: request to capture and check an image
     public static final String ACTION_CAPTURE_AND_CHECK_IMAGE = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE";
     // Primary -> managed intent: request to capture and check a video with custom output path
@@ -226,14 +224,8 @@
                 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
                 NotificationManager.IMPORTANCE_DEFAULT));
 
-        // we are explicitly started by {@link DeviceAdminTestReceiver} after a successful provisioning.
-        if (action.equals(ACTION_PROFILE_PROVISIONED)) {
-            // Jump back to CTS verifier with result.
-            Intent response = new Intent(ACTION_PROFILE_OWNER_STATUS);
-            response.putExtra(EXTRA_PROVISIONED, isProfileOwner());
-            new ByodFlowTestHelper(this).startActivityInPrimary(response);
-            // Queried by CtsVerifier in the primary side using startActivityForResult.
-        } else if (action.equals(ACTION_QUERY_PROFILE_OWNER)) {
+        // Queried by CtsVerifier in the primary side using startActivityForResult.
+        if (action.equals(ACTION_QUERY_PROFILE_OWNER)) {
             Intent response = new Intent();
             response.putExtra(EXTRA_PROVISIONED, isProfileOwner());
             setResult(RESULT_OK, response);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 9cff9d2..cb24dad 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -195,10 +195,6 @@
         dpm.addCrossProfileIntentFilter(getWho(context), filter,
                 DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
-        Intent intent = new Intent(context, ByodHelperActivity.class);
-        intent.setAction(ByodHelperActivity.ACTION_PROFILE_PROVISIONED);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
         // Disable the work profile instance of this activity, because it is a helper activity for
         // the work -> primary direction.
         context.getPackageManager().setComponentEnabledSetting(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
index 54c9305..b8ab5c4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
@@ -29,6 +29,7 @@
 import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState
         .WAITING_FOR_USER_INPUT;
 
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -64,6 +65,8 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -136,8 +139,8 @@
 
                 @Override
                 public void testCompleted(MultiNetworkValidator validator) {
-                    if (validator == mMultiNetworkValidators[mMultiNetworkValidators.length
-                            - 1]) {
+                    if (validator == mMultiNetworkValidators.get(mMultiNetworkValidators.size()
+                            - 1)) {
                         // Done all tests.
                         boolean passed = true;
                         for (MultiNetworkValidator multiNetworkValidator :
@@ -148,9 +151,9 @@
                     } else if (!validator.mTestResult) {
                         setTestResultAndFinish(false);
                     } else {
-                        for (int i = 0; i < mMultiNetworkValidators.length; i++) {
-                            if (mMultiNetworkValidators[i] == validator) {
-                                mCurrentValidator = mMultiNetworkValidators[i + 1];
+                        for (int i = 0; i < mMultiNetworkValidators.size(); i++) {
+                            if (mMultiNetworkValidators.get(i) == validator) {
+                                mCurrentValidator = mMultiNetworkValidators.get(i + 1);
                                 mTestNameView.setText(mCurrentValidator.mTestDescription);
                                 mCurrentValidator.startTest();
                                 break;
@@ -159,14 +162,7 @@
                     }
                 }
             };
-    private final MultiNetworkValidator[] mMultiNetworkValidators = {
-            new ConnectToWifiWithNoInternetValidator(
-                    R.string.multinetwork_connectivity_test_1_desc),
-            new LegacyConnectToWifiWithNoInternetValidator(
-                    R.string.multinetwork_connectivity_test_2_desc),
-            new LegacyConnectToWifiWithIntermittentInternetValidator(
-                    R.string.multinetwork_connectivity_test_3_desc)
-    };
+    private List<MultiNetworkValidator> mMultiNetworkValidators = Collections.emptyList();
     private final Runnable mTimeToCompletionRunnable = new Runnable() {
         @Override
         public void run() {
@@ -200,6 +196,8 @@
         super.onCreate(savedInstanceState);
         mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        mMultiNetworkValidators = createMultiNetworkValidators();
+
         recordCurrentWifiState();
         setupUserInterface();
         setupBroadcastReceivers();
@@ -228,6 +226,26 @@
         }
     }
 
+    private List<MultiNetworkValidator> createMultiNetworkValidators() {
+        MultiNetworkValidator[] allValidators = {
+            new ConnectToWifiWithNoInternetValidator(
+                    R.string.multinetwork_connectivity_test_1_desc),
+            new LegacyConnectToWifiWithNoInternetValidator(
+                    R.string.multinetwork_connectivity_test_2_desc),
+            new LegacyConnectToWifiWithIntermittentInternetValidator(
+                    R.string.multinetwork_connectivity_test_3_desc)
+        };
+
+        List<MultiNetworkValidator> result = new ArrayList<>();
+        boolean isLowRamDevice = isLowRamDevice();
+        for (MultiNetworkValidator validator : allValidators) {
+          if (!isLowRamDevice || validator.shouldRunOnLowRamDevice()) {
+            result.add(validator);
+          }
+        }
+        return result;
+    }
+
     private void restoreOriginalWifiState() {
         if (mRecordedWifiConfiguration >= 0) {
             mWifiManager.enableNetwork(mRecordedWifiConfiguration, true);
@@ -235,6 +253,11 @@
     }
 
     private boolean requestSystemAlertWindowPerimissionIfRequired() {
+        if (isLowRamDevice()) {
+          // For low ram devices, we won't run tests that depend on this permission.
+          return true;
+        }
+
         boolean hadPermission = false;
         if (!Settings.canDrawOverlays(this)) {
             AlertDialog alertDialog = new AlertDialog.Builder(this)
@@ -360,11 +383,11 @@
             return;
         }
 
-        for (int i = 0; i < mMultiNetworkValidators.length; i++) {
-            if (mMultiNetworkValidators[i].mValidatorState != COMPLETED) {
-                mCurrentValidator = mMultiNetworkValidators[i];
-                break;
-            }
+        for (MultiNetworkValidator multiNetworkValidator : mMultiNetworkValidators) {
+          if (multiNetworkValidator.mValidatorState != COMPLETED) {
+            mCurrentValidator = multiNetworkValidator;
+            break;
+          }
         }
         if (mCurrentValidator != null) {
             mTestNameView.setText(mCurrentValidator.mTestDescription);
@@ -393,7 +416,7 @@
             mStartButton.setText(R.string.multinetwork_connectivity_test_rerun);
             mStartButton.setEnabled(true);
             rerunMultinetworkTests();
-            mCurrentValidator = mMultiNetworkValidators[0];
+            mCurrentValidator = mMultiNetworkValidators.get(0);
         }
     }
 
@@ -479,6 +502,12 @@
         mStartButton.setText("--");
     }
 
+    private boolean isLowRamDevice() {
+        ActivityManager activityManager =
+            (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        return activityManager.isLowRamDevice();
+    }
+
     /**
      * Manage the connectivity state for each MultinetworkValidation.
      */
@@ -613,6 +642,7 @@
         final String mTestName;
         final MultinetworkTestCallback mTestCallback;
         final TestConnectivityState mConnectivityState;
+        final boolean mRunTestOnLowMemoryDevices;
 
         int mTestDescription;
         boolean mTestResult = false;
@@ -621,12 +651,15 @@
         int mTestProgressMessage;
 
         MultiNetworkValidator(MultinetworkTestCallback testCallback,
-                String testName, int testDescription) {
+                String testName,
+                int testDescription,
+                boolean runTestOnLowMemoryDevices) {
             mTestCallback = testCallback;
             mTestName = testName;
             mTestDescription = testDescription;
             mConnectivityState = new TestConnectivityState(this);
             mValidatorState = NOT_STARTED;
+            mRunTestOnLowMemoryDevices = runTestOnLowMemoryDevices;
         }
 
         /** Start test if not started. */
@@ -717,6 +750,10 @@
         void onWifiNetworkUnavailable() {
             endTest(false, R.string.multinetwork_status_wifi_connect_timed_out);
         }
+
+        boolean shouldRunOnLowRamDevice() {
+          return mRunTestOnLowMemoryDevices;
+        }
     }
 
     /**
@@ -726,7 +763,10 @@
     private class LegacyConnectToWifiWithNoInternetValidator extends MultiNetworkValidator {
 
         LegacyConnectToWifiWithNoInternetValidator(int description) {
-            super(mMultinetworkTestCallback, "legacy_no_internet_test", description);
+            super(mMultinetworkTestCallback,
+                "legacy_no_internet_test",
+                description,
+                /* runTestOnLowMemoryDevices = */ false);
         }
 
 
@@ -788,7 +828,10 @@
         Network mWifiNetwork;
 
         LegacyConnectToWifiWithIntermittentInternetValidator(int description) {
-            super(mMultinetworkTestCallback, "legcay_no_internet_test", description);
+            super(mMultinetworkTestCallback,
+                "legacy_no_internet_test",
+                description,
+                /* runTestOnLowMemoryDevices = */ false);
         }
 
         @Override
@@ -900,7 +943,10 @@
     private class ConnectToWifiWithNoInternetValidator extends MultiNetworkValidator {
 
         ConnectToWifiWithNoInternetValidator(int description) {
-            super(mMultinetworkTestCallback, "no_internet_test", description);
+            super(mMultinetworkTestCallback,
+                "no_internet_test",
+                description,
+                /* runTestOnLowMemoryDevices = */ true);
         }
 
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index 34c18de..6e54ef6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -32,8 +32,7 @@
  */
 public class AccelerometerMeasurementTestActivity extends SensorCtsVerifierTestActivity {
     public AccelerometerMeasurementTestActivity() {
-        super(AccelerometerMeasurementTestActivity.class);
-        mEnableRetry = true;
+        super(AccelerometerMeasurementTestActivity.class, true);
     }
 
     public String testFaceUp() throws Throwable {
@@ -109,12 +108,9 @@
 
     private String delayedVerifyMeasurements(int descriptionResId, float ... expectations)
             throws Throwable {
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(descriptionResId);
-            logger.logWaitForSound();
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(descriptionResId);
+        getTestLogger().logWaitForSound();
         Thread.sleep(TimeUnit.MILLISECONDS.convert(7, TimeUnit.SECONDS));
 
         try {
@@ -126,12 +122,8 @@
 
     private String verifyMeasurements(int descriptionResId, float ... expectations)
             throws Throwable {
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(descriptionResId);
-            logger.logInstructions(R.string.snsr_device_steady);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(descriptionResId, R.string.snsr_device_steady);
 
         return verifyMeasurements(expectations);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index eaa4924..9689193 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -36,8 +36,7 @@
  */
 public class BatchingTestActivity extends SensorCtsVerifierTestActivity {
     public BatchingTestActivity() {
-        super(BatchingTestActivity.class);
-        mEnableRetry = true;
+        super(BatchingTestActivity.class, true);
     }
 
     private static final int SENSOR_BATCHING_RATE_US = SensorManager.SENSOR_DELAY_FASTEST;
@@ -122,10 +121,8 @@
 
     private String runBatchTest(int sensorType, int maxBatchReportLatencySec, int instructionsResId)
             throws Throwable {
-        if (!mShouldRetry) {
-            getTestLogger().logInstructions(instructionsResId);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(instructionsResId);
 
         int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
         TestSensorEnvironment environment = new TestSensorEnvironment(
@@ -142,10 +139,8 @@
 
     private String runFlushTest(int sensorType, int maxBatchReportLatencySec, int instructionsResId)
             throws Throwable {
-        if (!mShouldRetry) {
-            getTestLogger().logInstructions(instructionsResId);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(instructionsResId);
 
         int maxBatchReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(maxBatchReportLatencySec);
         TestSensorEnvironment environment = new TestSensorEnvironment(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 368eb66..39cdfb0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -47,8 +47,7 @@
     private final GLRotationGuideRenderer mRenderer = new GLRotationGuideRenderer();
 
     public GyroscopeMeasurementTestActivity() {
-        super(GyroscopeMeasurementTestActivity.class);
-        mEnableRetry = true;
+        super(GyroscopeMeasurementTestActivity.class, true);
     }
 
     @Override
@@ -110,12 +109,9 @@
     public String testCalibratedAndUncalibrated() throws Throwable {
         setRendererRotation(Z_AXIS, false);
 
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(R.string.snsr_keep_device_rotating_clockwise);
-            waitForUserToBegin();
-        }
-        logger.logWaitForSound();
+        setFirstExecutionInstruction(R.string.snsr_keep_device_rotating_clockwise);
+
+        getTestLogger().logWaitForSound();
 
         TestSensorEnvironment calibratedEnvironment = new TestSensorEnvironment(
                 getApplicationContext(),
@@ -150,12 +146,9 @@
             throws Throwable {
         setRendererRotation(rotationAxis, expectationDeg >= 0);
 
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(instructionsResId);
-            waitForUserToBegin();
-        }
-        logger.logWaitForSound();
+        setFirstExecutionInstruction(instructionsResId);
+
+        getTestLogger().logWaitForSound();
 
         TestSensorEnvironment environment = new TestSensorEnvironment(
                 getApplicationContext(),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index e747784..e7e55f2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -54,8 +54,7 @@
  */
 public class SignificantMotionTestActivity extends SensorCtsVerifierTestActivity {
     public SignificantMotionTestActivity() {
-        super(SignificantMotionTestActivity.class);
-        mEnableRetry = true;
+        super(SignificantMotionTestActivity.class, true);
     }
 
     // acceptable time difference between event time and system time
@@ -141,15 +140,12 @@
 
     @SuppressWarnings("unused")
     public String testTriggerDeactivation() throws Throwable {
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(R.string.snsr_significant_motion_test_deactivation);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(R.string.snsr_significant_motion_test_deactivation);
 
         TriggerVerifier verifier = new TriggerVerifier();
         mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
-        logger.logWaitForSound();
+        getTestLogger().logWaitForSound();
 
         String result;
         try {
@@ -192,11 +188,9 @@
 
     @SuppressWarnings("unused")
     public String testAPWakeUpOnSMDTrigger() throws Throwable {
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(R.string.snsr_significant_motion_ap_suspend);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(R.string.snsr_significant_motion_ap_suspend);
+
         mVerifier = new TriggerVerifier();
         mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
         long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
@@ -247,11 +241,8 @@
             boolean isMotionExpected,
             boolean cancelEventNotification,
             boolean vibrate) throws Throwable {
-        SensorTestLogger logger = getTestLogger();
-        if (!mShouldRetry) {
-            logger.logInstructions(instructionsResId);
-            waitForUserToBegin();
-        }
+
+        setFirstExecutionInstruction(instructionsResId);
 
         if (vibrate) {
             vibrate(VIBRATE_DURATION_MILLIS);
@@ -267,7 +258,7 @@
                     getString(R.string.snsr_significant_motion_cancelation),
                     mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion));
         }
-        logger.logWaitForSound();
+        getTestLogger().logWaitForSound();
 
         String result;
         try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index 84c3ef7..966fc7d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -97,13 +97,9 @@
     private GLSurfaceView mGLSurfaceView;
     private boolean mUsingGlSurfaceView;
 
-    // Flag for sensor tests with retry.
-    protected boolean mEnableRetry = false;
     // Flag for Retry button appearance.
-    protected boolean mShouldRetry = false;
-    // Flag for the last sub-test to show Finish button.
-    protected boolean mIsLastSubtest = false;
-    protected int mRetryCount = 0;
+    private boolean mShouldRetry = false;
+    private int mRetryCount = 0;
 
     /**
      * Constructor to be used by subclasses.
@@ -142,7 +138,7 @@
         mFailButton = (Button) findViewById(R.id.fail_button);
         mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
         mRetryButton = (Button) findViewById(R.id.retry_button);
-        mRetryButton.setOnClickListener(this);
+        mRetryButton.setOnClickListener(new retryButtonListener());
 
         mRetryButton.setVisibility(View.GONE);
         updateNextButton(false /*enabled*/);
@@ -173,14 +169,7 @@
 
     @Override
     public void onClick(View target) {
-        switch (target.getId()) {
-            case R.id.next_button:
-                mShouldRetry = false;
-                break;
-            case R.id.retry_button:
-                mShouldRetry = true;
-                break;
-        }
+        mShouldRetry = false;
 
         synchronized (mWaitForUserLatches) {
             for (CountDownLatch latch : mWaitForUserLatches) {
@@ -190,6 +179,22 @@
         }
     }
 
+    private class retryButtonListener implements View.OnClickListener {
+
+        @Override
+        public void onClick(View v) {
+            mShouldRetry = true;
+            ++mRetryCount;
+
+            synchronized (mWaitForUserLatches) {
+                for (CountDownLatch latch : mWaitForUserLatches) {
+                    latch.countDown();
+                }
+                mWaitForUserLatches.clear();
+            }
+        }
+    }
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         mActivityResultMultiplexedLatch.onActivityResult(requestCode, resultCode);
@@ -273,6 +278,13 @@
      */
     protected abstract SensorTestDetails executeTests() throws InterruptedException;
 
+    /**
+     * Get mShouldRetry to check if test is required to retry.
+     */
+    protected boolean getShouldRetry() {
+        return mShouldRetry;
+    }
+
     @Override
     public SensorTestLogger getTestLogger() {
         return mTestLogger;
@@ -312,8 +324,12 @@
         }
 
         mTestLogger.logInstructions(waitMessageResId);
+        setNextButtonText(waitMessageResId);
+
+        updateRetryButton(true);
         updateNextButton(true);
         latch.await();
+        updateRetryButton(false);
         updateNextButton(false);
     }
 
@@ -326,14 +342,18 @@
 
     /**
      * Waits for the operator to acknowledge to retry execution.
-     * If the execution is for the last subtest, will notify user by Finish button.
      */
     protected void waitForUserToRetry() throws InterruptedException {
-        if (mIsLastSubtest) {
-            waitForUser(R.string.snsr_wait_to_finish);
-        } else {
-            waitForUser(R.string.snsr_wait_to_retry);
-        }
+        mShouldRetry = true;
+        waitForUser(R.string.snsr_wait_to_retry);
+    }
+
+    /**
+     * Waits for the operator to acknowledge to finish execution.
+     */
+    protected void waitForUserToFinish() throws InterruptedException {
+        mShouldRetry = true;
+        waitForUser(R.string.snsr_wait_to_finish);
     }
 
     /**
@@ -559,27 +579,31 @@
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mNextButton.setText(getNextButtonText());
-                updateRetryButton(enabled);
                 mNextButton.setEnabled(enabled);
             }
         });
     }
 
     /**
-     * Get the text for next button.
+     * Set the text for next button by instruction message.
      * During retry, next button text is changed to notify users.
+     *
+     * @param waitMessageResId The action requested to the operator.
      */
-    private int getNextButtonText() {
-        int nextButtonText = R.string.next_button_text;
-        if (mShouldRetry) {
-            if (mIsLastSubtest){
-                nextButtonText = R.string.finish_button_text;
-            } else {
+    private void setNextButtonText(int waitMessageResId) {
+        int nextButtonText;
+        switch (waitMessageResId) {
+            case R.string.snsr_wait_to_retry:
                 nextButtonText = R.string.fail_and_next_button_text;
-            }
+                break;
+            case R.string.snsr_wait_to_finish:
+                nextButtonText = R.string.finish_button_text;
+                break;
+            default:
+                nextButtonText = R.string.next_button_text;
+                break;
         }
-        return nextButtonText;
+        mNextButton.setText(nextButtonText);
     }
 
     /**
@@ -588,16 +612,22 @@
      *
      * @param enabled The status of button.
      */
-    private void updateRetryButton(boolean enabled) {
-        String showRetryCount = String.format(
-            "%s (%d)", getResources().getText(R.string.retry_button_text), mRetryCount);
-        if (mShouldRetry) {
-            mRetryButton.setText(showRetryCount);
-            mRetryButton.setVisibility(View.VISIBLE);
-            mRetryButton.setEnabled(enabled);
-        } else {
-            mRetryButton.setVisibility(View.GONE);
-        }
+    private void updateRetryButton(final boolean enabled) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mShouldRetry) {
+                    String showRetryCount = String.format(
+                        "%s (%d)", getResources().getText(R.string.retry_button_text), mRetryCount);
+                    mRetryButton.setText(showRetryCount);
+                    mRetryButton.setVisibility(View.VISIBLE);
+                    mRetryButton.setEnabled(enabled);
+                } else {
+                    mRetryButton.setVisibility(View.GONE);
+                    mRetryCount = 0;
+                }
+            }
+        });
     }
 
     private void enableTestResultButton(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
index 4401536..b65b595c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
@@ -40,6 +40,7 @@
     private volatile int mTestSkippedCounter;
     private volatile int mTestFailedCounter;
     private volatile ISensorTestNode mCurrentTestNode;
+    private volatile boolean mEnableRetry = false;
 
     /**
      * {@inheritDoc}
@@ -50,6 +51,20 @@
     }
 
     /**
+     * {@inheritDoc}
+     * Constructor to be used by subclasses.
+     *
+     * @param testClass   The class that contains the tests. It is dependant on test executor
+     *                    implemented by subclasses.
+     * @param enableRetry Subclass can enable retry mechanism for subtests.
+     */
+    protected SensorCtsVerifierTestActivity(
+        Class<? extends SensorCtsVerifierTestActivity> testClass, boolean enableRetry) {
+        super(testClass);
+        mEnableRetry = enableRetry;
+    }
+
+    /**
      * Executes Semi-automated Sensor tests.
      * Execution is driven by this class, and allows discovery of tests using reflection.
      */
@@ -59,22 +74,24 @@
         Iterator<Method> testMethodIt = findTestMethods().iterator();
         while (testMethodIt.hasNext()) {
             Method testMethod = testMethodIt.next();
-            mIsLastSubtest = !testMethodIt.hasNext();
-            mRetryCount = 0;
+            boolean isLastSubtest = !testMethodIt.hasNext();
             getTestLogger().logTestStart(testMethod.getName());
             SensorTestDetails testDetails = executeTest(testMethod);
             getTestLogger().logTestDetails(testDetails);
+
             // If tests enable retry and get failed result, trigger the retry process.
             while (mEnableRetry && testDetails.getResultCode().equals(ResultCode.FAIL)) {
-                mShouldRetry = true;
-                waitForUserToRetry();
-                if (!mShouldRetry) {
+                if (isLastSubtest) {
+                    waitForUserToFinish();
+                } else {
+                    waitForUserToRetry();
+                }
+                if (!getShouldRetry()) {
                     break;
                 }
                 mTestFailedCounter--;
                 testDetails = executeTest(testMethod);
                 getTestLogger().logTestDetails(testDetails);
-                mRetryCount++;
             }
         }
         return new SensorTestDetails(
@@ -151,4 +168,19 @@
             return mTestClass.getSimpleName() + "_" + mTestMethod.getName();
         }
     }
+
+    /**
+     * Show the instruction for the first time execution and wait for user to begin the test.
+     *
+     * @param descriptionResId The description for the first time execution.
+     */
+    protected void setFirstExecutionInstruction(int ... descriptionResId) throws Throwable {
+        if (!getShouldRetry()) {
+            SensorTestLogger logger = getTestLogger();
+            for (int id : descriptionResId) {
+                logger.logInstructions(id);
+            }
+            waitForUserToBegin();
+        }
+    }
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index 772009d..c01efd2 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -20,6 +20,7 @@
 import android.drm.DrmConvertedStatus;
 import android.drm.DrmManagerClient;
 import android.graphics.ImageFormat;
+import android.graphics.Rect;
 import android.media.Image;
 import android.media.Image.Plane;
 import android.media.MediaCodec;
@@ -1168,36 +1169,46 @@
 
         MessageDigest md = MessageDigest.getInstance("MD5");
 
-        int imageWidth = image.getWidth();
-        int imageHeight = image.getHeight();
+        Rect crop = image.getCropRect();
+        int cropLeft = crop.left;
+        int cropRight = crop.right;
+        int cropTop = crop.top;
+        int cropBottom = crop.bottom;
+
+        int imageWidth = cropRight - cropLeft;
+        int imageHeight = cropBottom - cropTop;
 
         Image.Plane[] planes = image.getPlanes();
         for (int i = 0; i < planes.length; ++i) {
             ByteBuffer buf = planes[i].getBuffer();
 
-            int width, height, rowStride, pixelStride, x, y;
+            int width, height, rowStride, pixelStride, x, y, top, left;
             rowStride = planes[i].getRowStride();
             pixelStride = planes[i].getPixelStride();
             if (i == 0) {
                 width = imageWidth;
                 height = imageHeight;
+                left = cropLeft;
+                top = cropTop;
             } else {
                 width = imageWidth / 2;
                 height = imageHeight /2;
+                left = cropLeft / 2;
+                top = cropTop / 2;
             }
             // local contiguous pixel buffer
             byte[] bb = new byte[width * height];
             if (buf.hasArray()) {
                 byte b[] = buf.array();
-                int offs = buf.arrayOffset();
+                int offs = buf.arrayOffset() + left * pixelStride;
                 if (pixelStride == 1) {
                     for (y = 0; y < height; ++y) {
-                        System.arraycopy(bb, y * width, b, y * rowStride + offs, width);
+                        System.arraycopy(bb, y * width, b, (top + y) * rowStride + offs, width);
                     }
                 } else {
                     // do it pixel-by-pixel
                     for (y = 0; y < height; ++y) {
-                        int lineOffset = offs + y * rowStride;
+                        int lineOffset = offs + (top + y) * rowStride;
                         for (x = 0; x < width; ++x) {
                             bb[y * width + x] = b[lineOffset + x * pixelStride];
                         }
@@ -1207,7 +1218,7 @@
                 int pos = buf.position();
                 if (pixelStride == 1) {
                     for (y = 0; y < height; ++y) {
-                        buf.position(pos + y * rowStride);
+                        buf.position(pos + left + (top + y) * rowStride);
                         buf.get(bb, y * width, width);
                     }
                 } else {
@@ -1215,7 +1226,7 @@
                     byte[] lb = new byte[rowStride];
                     // do it pixel-by-pixel
                     for (y = 0; y < height; ++y) {
-                        buf.position(pos + y * rowStride);
+                        buf.position(pos + left * pixelStride + (top + y) * rowStride);
                         // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
                         buf.get(lb, 0, pixelStride * (width - 1) + 1);
                         for (x = 0; x < width; ++x) {
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredFeatureRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredFeatureRule.java
index 0ab94d6..44571d1 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredFeatureRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredFeatureRule.java
@@ -55,6 +55,11 @@
         };
     }
 
+    @Override
+    public String toString() {
+        return "RequiredFeatureRule[" + mFeature + "]";
+    }
+
     public static boolean hasFeature(String feature) {
         return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(feature);
     }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredServiceRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredServiceRule.java
index 96dbcd8..bbfa2db 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredServiceRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredServiceRule.java
@@ -75,4 +75,9 @@
             return false;
         }
     }
+
+    @Override
+    public String toString() {
+        return "RequiredServiceRule[" + mService + "]";
+    }
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
new file mode 100644
index 0000000..ead59943
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.compatibility.common.util;
+
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Custom JUnit4 rule that does not run a test case if the device does not define the given system
+ * resource.
+ */
+public class RequiredSystemResourceRule implements TestRule {
+
+    private static final String TAG = "RequiredSystemResourceRule";
+
+    @NonNull private final String mName;
+    private final boolean mHasResource;
+
+    /**
+     * Creates a rule for the given system resource.
+     *
+     * @param resourceId resource per se
+     * @param name resource name used for debugging purposes
+     */
+    public RequiredSystemResourceRule(@NonNull String name) {
+        mName = name;
+        mHasResource = !TextUtils.isEmpty(getSystemResource(name));
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                if (!mHasResource) {
+                    Log.d(TAG, "skipping "
+                            + description.getClassName() + "#" + description.getMethodName()
+                            + " because device does not have system resource '" + mName + "'");
+                    return;
+                }
+                base.evaluate();
+            }
+        };
+    }
+
+    /**
+     * Gets the given system resource.
+     */
+    @Nullable
+    public static String getSystemResource(@NonNull String name) {
+        try {
+            final int resourceId = Resources.getSystem().getIdentifier(name, "string", "android");
+            return Resources.getSystem().getString(resourceId);
+        } catch (Exception e) {
+            Log.e(TAG, "could not get value of resource '" + name + "': ", e);
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "RequiredSystemResourceRule[" + mName + "]";
+    }
+}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
new file mode 100644
index 0000000..ead59943
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.compatibility.common.util;
+
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Custom JUnit4 rule that does not run a test case if the device does not define the given system
+ * resource.
+ */
+public class RequiredSystemResourceRule implements TestRule {
+
+    private static final String TAG = "RequiredSystemResourceRule";
+
+    @NonNull private final String mName;
+    private final boolean mHasResource;
+
+    /**
+     * Creates a rule for the given system resource.
+     *
+     * @param resourceId resource per se
+     * @param name resource name used for debugging purposes
+     */
+    public RequiredSystemResourceRule(@NonNull String name) {
+        mName = name;
+        mHasResource = !TextUtils.isEmpty(getSystemResource(name));
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                if (!mHasResource) {
+                    Log.d(TAG, "skipping "
+                            + description.getClassName() + "#" + description.getMethodName()
+                            + " because device does not have system resource '" + mName + "'");
+                    return;
+                }
+                base.evaluate();
+            }
+        };
+    }
+
+    /**
+     * Gets the given system resource.
+     */
+    @Nullable
+    public static String getSystemResource(@NonNull String name) {
+        try {
+            final int resourceId = Resources.getSystem().getIdentifier(name, "string", "android");
+            return Resources.getSystem().getString(resourceId);
+        } catch (Exception e) {
+            Log.e(TAG, "could not get value of resource '" + name + "': ", e);
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "RequiredSystemResourceRule[" + mName + "]";
+    }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AuthBoundKeyTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AuthBoundKeyTest.java
index 2597020..2197a66 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AuthBoundKeyTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AuthBoundKeyTest.java
@@ -52,7 +52,7 @@
         new InstallMultiple().addApk(APK).run();
         getDevice().executeShellCommand("cmd lock_settings set-pin 1234");
         runDeviceTests(PKG, CLASS, "testGenerateAuthBoundKey");
-        getDevice().executeShellCommand("cmd lock_settings clear --old 1234 --user 0");
+        getDevice().executeShellCommand("cmd lock_settings clear --old 1234");
         runDeviceTests(PKG, CLASS, "testUseKey");
         getDevice().executeShellCommand("cmd lock_settings set-pin 12345");
         getDevice().executeShellCommand("input keyevent 26");  // Screen on
@@ -63,7 +63,7 @@
         try {
             runDeviceTests(PKG, CLASS, "testUseKey");
         } finally {
-            getDevice().executeShellCommand("cmd lock_settings clear --old 12345 --user 0");
+            getDevice().executeShellCommand("cmd lock_settings clear --old 12345");
         }
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index 2723a6d..b2f9f3f 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -408,10 +408,10 @@
             long start = System.currentTimeMillis();
             while (permissionView == null && start + RETRY_TIMEOUT > System.currentTimeMillis()) {
                 permissionView = getUiDevice().wait(Until.findObject(By.text(permissionLabel)),
-                        GLOBAL_TIMEOUT_MILLIS);
+                        IDLE_TIMEOUT_MILLIS);
 
                 if (permissionView == null) {
-                    getUiDevice().findObject(By.res("android:id/list_container"))
+                    getUiDevice().findObject(By.scrollable(true))
                             .scroll(Direction.DOWN, 1);
                 }
             }
diff --git a/hostsidetests/backup/OWNERS b/hostsidetests/backup/OWNERS
index 3637e32..c28c4d8 100644
--- a/hostsidetests/backup/OWNERS
+++ b/hostsidetests/backup/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 41666
 # Use this reviewer by default.
 br-framework-team+reviews@google.com
 
diff --git a/hostsidetests/backup/RestoreSessionTest/Android.bp b/hostsidetests/backup/RestoreSessionTest/Android.bp
new file mode 100644
index 0000000..a3ac883
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+    name: "CtsRestoreSessionApp",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+        "truth-prebuilt",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml b/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml
new file mode 100644
index 0000000..130e3aa
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.restoresessionapp">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+    <application
+        android:label="RestoreSessionApp"
+        android:allowBackup="true">
+
+        <uses-library android:name="android.test.runner" />
+
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.restoresessionapp" />
+</manifest>
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java
new file mode 100644
index 0000000..85b05d3
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BaseRestoreSessionAppTest {
+    private static final String SHARED_PREFERENCES_FILE = "restore_session_app_prefs";
+
+    private SharedPreferences mPreferences;
+
+    @Before
+    public void setUp() {
+        mPreferences =
+                getTargetContext()
+                        .getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+    }
+
+    protected void clearSharedPrefs() {
+        mPreferences.edit().clear().commit();
+    }
+
+    protected void checkSharedPrefsDontExist(String prefKey) {
+        assertThat(mPreferences.getInt(prefKey, 0)).isEqualTo(0);
+    }
+
+    protected void saveValuesToSharedPrefs(String prefKey, int prefValue) {
+        mPreferences.edit().putInt(prefKey, prefValue).commit();
+    }
+
+    protected void checkSharedPrefsExist(String prefKey, int prefValue) {
+        assertThat(mPreferences.getInt(prefKey, 0)).isEqualTo(prefValue);
+    }
+}
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
new file mode 100644
index 0000000..06b9ae0
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.RestoreObserver;
+import android.app.backup.RestoreSession;
+import android.app.backup.RestoreSet;
+import android.content.Context;
+import android.os.Bundle;
+
+import android.platform.test.annotations.AppModeFull;
+import androidx.test.runner.AndroidJUnit4;
+
+// import com.android.compatibility.common.util.SystemUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class RestoreSessionTest {
+    private static final String PACKAGE_1 = "android.cts.backup.restoresessionapp1";
+    private static final String PACKAGE_2 = "android.cts.backup.restoresessionapp2";
+    private static final String PACKAGE_3 = "android.cts.backup.restoresessionapp3";
+
+    private static final int RESTORE_TIMEOUT_SECONDS = 10;
+
+    private BackupManager mBackupManager;
+    private Set<String> mRestorePackages;
+    private Set<String> mNonRestorePackages;
+    private CountDownLatch mRestoreObserverLatch;
+    private RestoreSession mRestoreSession;
+    private UiAutomation mUiAutomation;
+    private long mRestoreToken;
+
+    private final RestoreObserver mRestoreObserver =
+            new RestoreObserver() {
+                @Override
+                public void restoreSetsAvailable(RestoreSet[] result) {
+                    super.restoreSetsAvailable(result);
+
+                    long token = 0L;
+
+                    for (RestoreSet restoreSet : result) {
+                        long restoreToken = restoreSet.token;
+                        if (doesRestoreSetContainAllPackages(restoreToken, mRestorePackages)
+                                && doesRestoreSetContainAllPackages(
+                                        restoreToken, mNonRestorePackages)) {
+                            token = restoreSet.token;
+                            break;
+                        }
+                    }
+
+                    mRestoreToken = token;
+
+                    mRestoreObserverLatch.countDown();
+                }
+
+                @Override
+                public void restoreStarting(int numPackages) {
+                    super.restoreStarting(numPackages);
+
+                    assertEquals(
+                            "Wrong number of packages in the restore set",
+                            mRestorePackages.size(),
+                            numPackages);
+                    mRestoreObserverLatch.countDown();
+                }
+
+                @Override
+                public void onUpdate(int nowBeingRestored, String currentPackage) {
+                    super.onUpdate(nowBeingRestored, currentPackage);
+
+                    assertTrue(
+                            "Restoring package that is not in mRestorePackages",
+                            mRestorePackages.contains(currentPackage));
+                    mRestoreObserverLatch.countDown();
+                }
+
+                @Override
+                public void restoreFinished(int error) {
+                    super.restoreFinished(error);
+
+                    assertEquals(
+                            "Restore finished with error: " + error, BackupManager.SUCCESS, error);
+                    mRestoreSession.endRestoreSession();
+                    mRestoreObserverLatch.countDown();
+                }
+            };
+
+    @Before
+    public void setUp() throws InterruptedException {
+        Context context = getTargetContext();
+        mBackupManager = new BackupManager(context);
+
+        mRestorePackages = new HashSet<>();
+        mRestorePackages.add(PACKAGE_1);
+        mRestorePackages.add(PACKAGE_2);
+
+        mNonRestorePackages = new HashSet<>();
+        mNonRestorePackages.add(PACKAGE_3);
+
+        mRestoreToken = 0L;
+
+        mUiAutomation = getInstrumentation().getUiAutomation();
+        mUiAutomation.adoptShellPermissionIdentity();
+
+        loadAvailableRestoreSets();
+    }
+
+    @After
+    public void tearDown() {
+        mUiAutomation.dropShellPermissionIdentity();
+    }
+
+    /**
+     * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+     * {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
+     */
+    @Test
+    public void testRestorePackages() throws InterruptedException {
+        testRestorePackagesInternal(false);
+    }
+
+    /**
+     * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+     * {@link RestoreSession#restorePackages(long, RestoreObserver, Set, BackupManagerMonitor)}
+     */
+    @Test
+    public void testRestorePackagesWithMonitorParam() throws InterruptedException {
+        testRestorePackagesInternal(true);
+    }
+
+    private void testRestorePackagesInternal(boolean useMonitorParam) throws InterruptedException {
+        // Wait for the callbacks from RestoreObserver: one for each package from
+        // mRestorePackages plus restoreStarting and restoreFinished.
+        mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+        CountDownLatch backupMonitorLatch = null;
+        if (useMonitorParam) {
+            // Wait for the callbacks from BackupManagerMonitor: one for each package.
+            backupMonitorLatch = new CountDownLatch(mRestorePackages.size());
+            mRestoreSession.restorePackages(
+                    mRestoreToken,
+                    mRestoreObserver,
+                    mRestorePackages,
+                    new TestBackupMonitor(backupMonitorLatch));
+        } else {
+            mRestoreSession.restorePackages(mRestoreToken, mRestoreObserver, mRestorePackages);
+        }
+
+        awaitResultAndAssertSuccess(mRestoreObserverLatch);
+        if (backupMonitorLatch != null) {
+            awaitResultAndAssertSuccess(backupMonitorLatch);
+        }
+    }
+
+    private void loadAvailableRestoreSets() throws InterruptedException {
+        // Wait for getAvailableRestoreSets to finish and the callback to be fired.
+        mRestoreObserverLatch = new CountDownLatch(1);
+        mRestoreSession = mBackupManager.beginRestoreSession();
+        assertEquals(
+                BackupManager.SUCCESS, mRestoreSession.getAvailableRestoreSets(mRestoreObserver));
+        awaitResultAndAssertSuccess(mRestoreObserverLatch);
+
+        assertNotEquals("Restore set not found", 0L, mRestoreToken);
+    }
+
+    private boolean doesRestoreSetContainAllPackages(long restoreToken, Set<String> packages) {
+        for (String restorePackage : packages) {
+            if (mBackupManager.getAvailableRestoreToken(restorePackage) != restoreToken) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void awaitResultAndAssertSuccess(CountDownLatch latch) throws InterruptedException {
+        boolean waitResult = latch.await(RESTORE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Restore timed out", waitResult);
+    }
+
+    private static class TestBackupMonitor extends BackupManagerMonitor {
+        private final CountDownLatch mLatch;
+
+        TestBackupMonitor(CountDownLatch latch) {
+            mLatch = latch;
+        }
+
+        @Override
+        public void onEvent(Bundle event) {
+            super.onEvent(event);
+
+            int eventType = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
+            assertEquals(
+                    "Unexpected event from BackupManagerMonitor: " + eventType,
+                    BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH,
+                    eventType);
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/hostsidetests/backup/restoresessionapp1/Android.bp b/hostsidetests/backup/restoresessionapp1/Android.bp
new file mode 100644
index 0000000..2eb5d0e
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+    name: "CtsRestoreSessionApp1",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "CtsRestoreSessionApp",
+    ],
+    srcs: [
+        "src/**/*.java"
+    ],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+    sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml
new file mode 100644
index 0000000..ae6b205
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.restoresessionapp1">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+    <application
+        android:label="RestoreSessionApp"
+        android:allowBackup="true">
+
+        <uses-library android:name="android.test.runner" />
+
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.restoresessionapp1" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java
new file mode 100644
index 0000000..f82d058
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp1;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+    private static final String SHARED_PREFERENCES_KEY = "test_key_1";
+    private static final int SHARED_PREFERENCES_VALUE = 123;
+
+    @Test
+    public void testClearSharedPrefs() {
+        clearSharedPrefs();
+    }
+
+    @Test
+    public void testCheckSharedPrefsDontExist() {
+        checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+    }
+
+    @Test
+    public void testSaveValuesToSharedPrefs() {
+        saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+
+    @Test
+    public void testCheckSharedPrefsExist() {
+        checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+}
diff --git a/hostsidetests/backup/restoresessionapp2/Android.bp b/hostsidetests/backup/restoresessionapp2/Android.bp
new file mode 100644
index 0000000..e389054
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+    name: "CtsRestoreSessionApp2",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "CtsRestoreSessionApp",
+    ],
+    srcs: [
+        "src/**/*.java"
+    ],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+    sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml
new file mode 100644
index 0000000..757c801
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.restoresessionapp2">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+    <application
+        android:label="RestoreSessionApp"
+        android:allowBackup="true"/>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.restoresessionapp2" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java
new file mode 100644
index 0000000..458103b
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp2;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+    private static final String SHARED_PREFERENCES_KEY = "test_key_2";
+    private static final int SHARED_PREFERENCES_VALUE = 124;
+
+    @Test
+    public void testClearSharedPrefs() {
+        clearSharedPrefs();
+    }
+
+    @Test
+    public void testCheckSharedPrefsDontExist() {
+        checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+    }
+
+    @Test
+    public void testSaveValuesToSharedPrefs() {
+        saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+
+    @Test
+    public void testCheckSharedPrefsExist() {
+        checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+}
diff --git a/hostsidetests/backup/restoresessionapp3/Android.bp b/hostsidetests/backup/restoresessionapp3/Android.bp
new file mode 100644
index 0000000..1451823
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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.
+
+android_test_helper_app {
+    name: "CtsRestoreSessionApp3",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "CtsRestoreSessionApp",
+    ],
+    srcs: [
+        "src/**/*.java"
+    ],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+    ],
+    sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml
new file mode 100644
index 0000000..027cf68
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.restoresessionapp3">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+    <application
+        android:label="RestoreSessionApp"
+        android:allowBackup="true"/>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.restoresessionapp3" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java
new file mode 100644
index 0000000..b034e1a
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp3;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+    private static final String SHARED_PREFERENCES_KEY = "test_key_3";
+    private static final int SHARED_PREFERENCES_VALUE = 125;
+
+    @Test
+    public void testClearSharedPrefs() {
+        clearSharedPrefs();
+    }
+
+    @Test
+    public void testCheckSharedPrefsDontExist() {
+        checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+    }
+
+    @Test
+    public void testSaveValuesToSharedPrefs() {
+        saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+
+    @Test
+    public void testCheckSharedPrefsExist() {
+        checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
new file mode 100644
index 0000000..9c5d890
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 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.cts.backup;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.io.IOException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Optional;
+
+/**
+ * Tests for system APIs in {@link RestoreSession}
+ *
+ * <p>These tests use the local transport.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class RestoreSessionHostSideTest extends BaseBackupHostSideTest {
+    private static final int USER_SYSTEM = 0;
+    private static final String MAIN_TEST_APP_PKG = "android.cts.backup.restoresessionapp";
+    private static final String DEVICE_MAIN_TEST_CLASS_NAME =
+            MAIN_TEST_APP_PKG + ".RestoreSessionTest";
+    private static final String MAIN_TEST_APK = "CtsRestoreSessionApp.apk";
+
+    private static final String TEST_APP_PKG_PREFIX = "android.cts.backup.restoresessionapp";
+    private static final String TEST_APP_APK_PREFIX = "CtsRestoreSessionApp";
+    private static final int TEST_APPS_COUNT = 3;
+
+    private Optional<String> mOldTransport = Optional.empty();
+    private BackupUtils mBackupUtils;
+
+    /** Switch to local transport. */
+    @Before
+    public void setUp() throws Exception {
+        mBackupUtils = getBackupUtils();
+        mOldTransport = Optional.of(setBackupTransport(mBackupUtils.getLocalTransportName()));
+        installPackage(MAIN_TEST_APK);
+    }
+
+    /** Restore transport settings to original values. */
+    @After
+    public void tearDown() throws Exception {
+        if (mOldTransport.isPresent()) {
+            setBackupTransport(mOldTransport.get());
+            mOldTransport = Optional.empty();
+
+            uninstallPackage(MAIN_TEST_APK);
+        }
+    }
+
+    /** Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} */
+    @Test
+    public void testRestorePackages() throws Exception {
+        testRestorePackagesInternal("testRestorePackages");
+    }
+
+    /**
+     * Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set, BackupManagerMonitor)}
+     */
+    @Test
+    public void testRestorePackagesWithMonitorParam() throws Exception {
+        testRestorePackagesInternal("testRestorePackagesWithMonitorParam");
+    }
+
+    /**
+     *
+     *
+     * <ol>
+     *   <li>Install 3 test packages on the device
+     *   <li>Write dummy values to shared preferences for each package
+     *   <li>Backup each package (adb shell bmgr backupnow)
+     *   <li>Clear shared preferences for each package
+     *   <li>Run restore for 2 of the packages and verify that only they were restored
+     *   <li>Verify that shared preferences for the 2 packages are restored correctly
+     * </ol>
+     */
+    private void testRestorePackagesInternal(String deviceTestName) throws Exception {
+        installPackage(getApkNameForTestApp(1));
+        installPackage(getApkNameForTestApp(2));
+        installPackage(getApkNameForTestApp(3));
+        //
+        // Write dummy value to shared preferences for all test packages.
+        checkRestoreSessionDeviceTestForAllApps("testSaveValuesToSharedPrefs");
+        checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsExist");
+
+        // Backup all test packages.
+        mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(1));
+        mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(2));
+        mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(3));
+
+        // Clear shared preferences for all test packages.
+        checkRestoreSessionDeviceTestForAllApps("testClearSharedPrefs");
+        checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsDontExist");
+
+        runRestoreSessionDeviceTestAndAssertSuccess(
+                MAIN_TEST_APP_PKG, DEVICE_MAIN_TEST_CLASS_NAME, deviceTestName);
+
+        // Check that shared prefs are only restored (and restored correctly) for the first 2
+        // packages.
+        checkRestoreSessionDeviceTest(1, "testCheckSharedPrefsExist");
+        checkRestoreSessionDeviceTest(2, "testCheckSharedPrefsExist");
+        checkRestoreSessionDeviceTest(3, "testCheckSharedPrefsDontExist");
+
+        uninstallPackage(getPackageNameForTestApp(1));
+        uninstallPackage(getPackageNameForTestApp(2));
+        uninstallPackage(getPackageNameForTestApp(3));
+    }
+
+    /** Run the given device test for all test apps. */
+    private void checkRestoreSessionDeviceTestForAllApps(String testName)
+            throws DeviceNotAvailableException {
+        for (int appNumber = 1; appNumber <= TEST_APPS_COUNT; appNumber++) {
+            checkRestoreSessionDeviceTest(appNumber, testName);
+        }
+    }
+
+    /** Run device test with the given test name and test app number. */
+    private void checkRestoreSessionDeviceTest(int testAppNumber, String testName)
+            throws DeviceNotAvailableException {
+        String packageName = getPackageNameForTestApp(testAppNumber);
+        runRestoreSessionDeviceTestAndAssertSuccess(
+                packageName, packageName + ".RestoreSessionAppTest", testName);
+    }
+
+    private void runRestoreSessionDeviceTestAndAssertSuccess(
+            String packageName, String fullClassName, String testName)
+            throws DeviceNotAvailableException {
+        boolean result = runDeviceTests(packageName, fullClassName, testName);
+        assertTrue("Device test failed: " + testName, result);
+    }
+
+    private String getPackageNameForTestApp(int appNumber) {
+        return TEST_APP_PKG_PREFIX + appNumber;
+    }
+
+    private String getApkNameForTestApp(int appNumber) {
+        return TEST_APP_APK_PREFIX + appNumber + ".apk";
+    }
+
+    private String setBackupTransport(String transport) throws IOException {
+        return mBackupUtils.setBackupTransportForUser(transport, USER_SYSTEM);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index c8385d7..fedd10e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -85,6 +85,20 @@
         <activity
             android:name="com.android.cts.deviceandprofileowner.KeyManagementActivity"
             android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
+        <activity
+            android:name="com.android.cts.deviceandprofileowner.LockTaskUtilityActivity"/>
+        <activity
+            android:name="com.android.cts.deviceandprofileowner.LockTaskUtilityActivityIfWhitelisted"
+            android:launchMode="singleInstance"
+            android:lockTaskMode="if_whitelisted">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
     </application>
 
     <instrumentation
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AffiliationTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AffiliationTest.java
new file mode 100644
index 0000000..5b8f4fb
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AffiliationTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 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.deviceandprofileowner;
+
+import static com.android.cts.deviceandprofileowner.BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class AffiliationTest {
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mDevicePolicyManager = (DevicePolicyManager)
+                context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+    }
+
+    @Test
+    public void testSetAffiliationId1() {
+        setAffiliationIds(Collections.singleton("id.number.1"));
+    }
+
+    @Test
+    public void testSetAffiliationId2() {
+        setAffiliationIds(Collections.singleton("id.number.2"));
+    }
+
+    @Test
+    public void testLockTaskMethodsThrowExceptionIfUnaffiliated() {
+        checkLockTaskMethodsThrow();
+    }
+
+    /** Assumes that the calling user is already affiliated before calling this method */
+    @Test
+    public void testSetLockTaskPackagesClearedIfUserBecomesUnaffiliated() {
+        final String[] packages = {"package1", "package2"};
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_RECEIVER_COMPONENT, packages);
+        assertArrayEquals(packages,
+                mDevicePolicyManager.getLockTaskPackages(ADMIN_RECEIVER_COMPONENT));
+        assertTrue(mDevicePolicyManager.isLockTaskPermitted("package1"));
+        assertFalse(mDevicePolicyManager.isLockTaskPermitted("package3"));
+
+        final Set<String> previousAffiliationIds =
+                mDevicePolicyManager.getAffiliationIds(ADMIN_RECEIVER_COMPONENT);
+        try {
+            // Clearing affiliation ids for this user. Lock task methods unavailable.
+            setAffiliationIds(Collections.emptySet());
+            checkLockTaskMethodsThrow();
+            assertFalse(mDevicePolicyManager.isLockTaskPermitted("package1"));
+
+            // Affiliating the user again. Previously set packages have been cleared.
+            setAffiliationIds(previousAffiliationIds);
+            assertEquals(0,
+                    mDevicePolicyManager.getLockTaskPackages(ADMIN_RECEIVER_COMPONENT).length);
+            assertFalse(mDevicePolicyManager.isLockTaskPermitted("package1"));
+        } finally {
+            mDevicePolicyManager.setAffiliationIds(ADMIN_RECEIVER_COMPONENT,
+                    previousAffiliationIds);
+        }
+    }
+
+    private void setAffiliationIds(Set<String> ids) {
+        mDevicePolicyManager.setAffiliationIds(ADMIN_RECEIVER_COMPONENT, ids);
+        assertEquals(ids, mDevicePolicyManager.getAffiliationIds(ADMIN_RECEIVER_COMPONENT));
+    }
+
+    private void checkLockTaskMethodsThrow() {
+        try {
+            mDevicePolicyManager.setLockTaskPackages(ADMIN_RECEIVER_COMPONENT, new String[0]);
+            fail("setLockTaskPackages did not throw expected SecurityException");
+        } catch (SecurityException expected) {
+        }
+        try {
+            mDevicePolicyManager.getLockTaskPackages(ADMIN_RECEIVER_COMPONENT);
+            fail("getLockTaskPackages did not throw expected SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
new file mode 100644
index 0000000..932943a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -0,0 +1,290 @@
+/*
+ * 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 com.android.cts.deviceandprofileowner;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class that is meant to be driven from the host and can't be run alone, which is required
+ * for tests that include rebooting or other connection-breaking steps. For this reason, this class
+ * does not override tearDown and setUp just initializes the test state, changing nothing in the
+ * device. Therefore, the host is responsible for making sure the tests leave the device in a clean
+ * state after running.
+ */
+@RunWith(AndroidJUnit4.class)
+public class LockTaskHostDrivenTest extends BaseDeviceAdminTest {
+
+    private static final String TAG = LockTaskHostDrivenTest.class.getName();
+    private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 20000;  // 20 seconds
+    private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+    private static final String LOCK_TASK_ACTIVITY
+            = LockTaskUtilityActivityIfWhitelisted.class.getName();
+
+    private UiDevice mUiDevice;
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private ActivityManager mActivityManager;
+    private TelecomManager mTelcomManager;
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Before
+    public void setUp() {
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mContext = InstrumentationRegistry.getContext();
+        mPackageManager = mContext.getPackageManager();
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
+        mTelcomManager = mContext.getSystemService(TelecomManager.class);
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+    }
+
+    @Test
+    public void startLockTask() throws Exception {
+        Log.d(TAG, "startLockTask on host-driven test (no cleanup)");
+        setLockTaskPackages(mContext.getPackageName());
+        setDefaultHomeIntentReceiver();
+        launchLockTaskActivity();
+        mUiDevice.waitForIdle();
+    }
+
+    @Test
+    public void cleanupLockTask() {
+        Log.d(TAG, "cleanupLockTask on host-driven test");
+        mDevicePolicyManager.clearPackagePersistentPreferredActivities(
+                ADMIN_RECEIVER_COMPONENT,
+                mContext.getPackageName());
+        setLockTaskPackages();
+        mDevicePolicyManager.setLockTaskFeatures(ADMIN_RECEIVER_COMPONENT, 0);
+        // In case some activity is still in foreground
+        mUiDevice.pressHome();
+    }
+
+    /**
+     * On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
+     * assume it's finished. Therefore, only use it once in a given test.
+     */
+    @Test
+    public void testLockTaskIsActiveAndCantBeInterrupted() throws Exception {
+        Log.d(TAG, "testLockTaskIsActiveAndCantBeInterrupted on host-driven test");
+        waitAndCheckLockedActivityIsResumed();
+        checkLockedActivityIsRunning();
+
+        mUiDevice.pressBack();
+        mUiDevice.waitForIdle();
+        checkLockedActivityIsRunning();
+
+        mUiDevice.pressHome();
+        mUiDevice.waitForIdle();
+        checkLockedActivityIsRunning();
+
+        mUiDevice.pressRecentApps();
+        mUiDevice.waitForIdle();
+        checkLockedActivityIsRunning();
+
+        mUiDevice.waitForIdle();
+    }
+
+    @Test
+    public void testLockTaskIsExitedIfNotWhitelisted() throws Exception {
+        Log.d(TAG, "testLockTaskIsExitedIfNotWhitelisted on host-driven test");
+
+        // Whitelist this package
+        setLockTaskPackages(mContext.getPackageName());
+
+        // Launch lock task root activity
+        setDefaultHomeIntentReceiver();
+        launchLockTaskActivity();
+        waitAndCheckLockedActivityIsResumed();
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED, mActivityManager.getLockTaskModeState());
+
+        // Remove it from whitelist
+        setLockTaskPackages();
+        mUiDevice.waitForIdle();
+
+        // The activity should be finished and exit lock task mode
+        waitAndCheckLockedActivityIsPaused();
+        assertEquals(ActivityManager.LOCK_TASK_MODE_NONE, mActivityManager.getLockTaskModeState());
+    }
+
+    @Test
+    public void testLockTaskCanLaunchDefaultDialer() throws Exception {
+        if (!hasTelephonyFeature()) {
+            Log.d(TAG, "testLockTaskCanLaunchDefaultDialer skipped");
+            return;
+        }
+
+        Log.d(TAG, "testLockTaskCanLaunchDefaultDialer on host-driven test");
+
+        // Whitelist dialer package
+        String dialerPackage = mTelcomManager.getSystemDialerPackage();
+        assertNotNull(dialerPackage);
+        setLockTaskPackages(mContext.getPackageName(), dialerPackage);
+
+        // Launch lock task root activity
+        setDefaultHomeIntentReceiver();
+        launchLockTaskActivity();
+        waitAndCheckLockedActivityIsResumed();
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED, mActivityManager.getLockTaskModeState());
+
+        // Launch dialer
+        launchDialerIntoLockTaskMode(dialerPackage);
+
+        // Wait until dialer package starts
+        mUiDevice.wait(
+                Until.hasObject(By.pkg(dialerPackage).depth(0)),
+                ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+        mUiDevice.waitForIdle();
+        waitAndCheckLockedActivityIsPaused();
+
+        // But still in LockTask mode
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED,
+                mActivityManager.getLockTaskModeState());
+    }
+
+    @Test
+    public void testLockTaskCanLaunchEmergencyDialer() throws Exception {
+        if (!hasTelephonyFeature()) {
+            Log.d(TAG, "testLockTaskCanLaunchEmergencyDialer skipped");
+            return;
+        }
+
+        // Find dialer package
+        String dialerPackage = getEmergencyDialerPackageName();
+        if (dialerPackage == null || dialerPackage.isEmpty()) {
+            Log.d(TAG, "testLockTaskCanLaunchEmergencyDialer skipped since no emergency dialer");
+            return;
+        }
+
+        Log.d(TAG, "testLockTaskCanLaunchEmergencyDialer on host-driven test");
+
+        // Emergency dialer should be usable as long as keyguard feature is enabled
+        // regardless of the package whitelist
+        mDevicePolicyManager.setLockTaskFeatures(
+                ADMIN_RECEIVER_COMPONENT, DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD);
+        setLockTaskPackages(mContext.getPackageName());
+
+        // Launch lock task root activity
+        setDefaultHomeIntentReceiver();
+        launchLockTaskActivity();
+        waitAndCheckLockedActivityIsResumed();
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED, mActivityManager.getLockTaskModeState());
+
+        // Launch dialer
+        launchEmergencyDialer();
+
+        // Wait until dialer package starts
+        mUiDevice.wait(
+                Until.hasObject(By.pkg(dialerPackage).depth(0)),
+                ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+        mUiDevice.waitForIdle();
+        waitAndCheckLockedActivityIsPaused();
+
+        // But still in LockTask mode
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED,
+                mActivityManager.getLockTaskModeState());
+    }
+
+    private boolean hasTelephonyFeature() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
+                mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+    }
+
+    private void checkLockedActivityIsRunning() {
+        String activityName =
+                mActivityManager.getAppTasks().get(0).getTaskInfo().topActivity.getClassName();
+        assertEquals(LOCK_TASK_ACTIVITY, activityName);
+        assertEquals(
+                ActivityManager.LOCK_TASK_MODE_LOCKED, mActivityManager.getLockTaskModeState());
+    }
+
+    private void waitAndCheckLockedActivityIsResumed() throws Exception {
+        mUiDevice.waitForIdle();
+        assertTrue(
+                LockTaskUtilityActivity.waitUntilActivityResumed(ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+    }
+
+    private void waitAndCheckLockedActivityIsPaused() throws Exception {
+        mUiDevice.waitForIdle();
+        assertTrue(
+                LockTaskUtilityActivity.waitUntilActivityPaused(ACTIVITY_RESUMED_TIMEOUT_MILLIS));
+    }
+
+    private void launchDialerIntoLockTaskMode(String dialerPackage) {
+        Intent intent = new Intent(Intent.ACTION_DIAL)
+                .setPackage(dialerPackage)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Bundle options = ActivityOptions.makeBasic().setLockTaskEnabled(true).toBundle();
+        mContext.startActivity(intent, options);
+    }
+
+    private void launchEmergencyDialer() {
+        Intent intent = new Intent(ACTION_EMERGENCY_DIAL).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
+    private String getEmergencyDialerPackageName() {
+        Intent intent = new Intent(ACTION_EMERGENCY_DIAL).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        ResolveInfo dialerInfo =
+                mPackageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        return (dialerInfo != null) ? dialerInfo.activityInfo.packageName : null;
+    }
+
+    private void launchLockTaskActivity() {
+        Intent intent = new Intent(mContext, LockTaskUtilityActivityIfWhitelisted.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(LockTaskUtilityActivity.START_LOCK_TASK, true);
+        mContext.startActivity(intent);
+    }
+
+    private void setLockTaskPackages(String... packages) {
+        mDevicePolicyManager.setLockTaskPackages(ADMIN_RECEIVER_COMPONENT, packages);
+    }
+
+    private void setDefaultHomeIntentReceiver() {
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MAIN);
+        intentFilter.addCategory(Intent.CATEGORY_HOME);
+        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
+        mDevicePolicyManager.addPersistentPreferredActivity(
+                ADMIN_RECEIVER_COMPONENT, intentFilter,
+                new ComponentName(mContext.getPackageName(), LOCK_TASK_ACTIVITY));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
similarity index 97%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
index 6cfec10..37f1be3 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
 
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
@@ -23,10 +23,6 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.testng.Assert.assertThrows;
 
@@ -52,19 +48,18 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-public class LockTaskTest {
+public class LockTaskTest extends BaseDeviceAdminTest {
 
     private static final String TAG = "LockTaskTest";
 
     private static final String PACKAGE_NAME = LockTaskTest.class.getPackage().getName();
-    private static final ComponentName ADMIN_COMPONENT =
-            new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
+    private static final ComponentName ADMIN_COMPONENT = ADMIN_RECEIVER_COMPONENT;
     private static final String TEST_PACKAGE = "com.google.android.example.somepackage";
 
     private static final String UTILITY_ACTIVITY
-            = "com.android.cts.deviceowner.LockTaskUtilityActivity";
+            = "com.android.cts.deviceandprofileowner.LockTaskUtilityActivity";
     private static final String UTILITY_ACTIVITY_IF_WHITELISTED
-            = "com.android.cts.deviceowner.LockTaskUtilityActivityIfWhitelisted";
+            = "com.android.cts.deviceandprofileowner.LockTaskUtilityActivityIfWhitelisted";
 
     private static final String RECEIVER_ACTIVITY_PACKAGE_NAME =
             "com.android.cts.intent.receiver";
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivity.java
similarity index 68%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivity.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivity.java
index aeb8abd..7811437 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivity.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
 
 import android.app.Activity;
 import android.content.Intent;
@@ -28,11 +28,41 @@
     public static final String START_ACTIVITY = "startActivity";
     public static final String FINISH = "finish";
 
-    public static final String CREATE_ACTION = "com.android.cts.deviceowner.LOCK_TASK_CREATE";
-    public static final String DESTROY_ACTION = "com.android.cts.deviceowner.LOCK_TASK_DESTROY";
-    public static final String PAUSE_ACTION = "com.android.cts.deviceowner.LOCK_TASK_PAUSE";
-    public static final String RESUME_ACTION = "com.android.cts.deviceowner.LOCK_TASK_RESUME";
-    public static final String INTENT_ACTION = "com.android.cts.deviceowner.LOCK_TASK_INTENT";
+    public static final String CREATE_ACTION = "com.android.cts.deviceandprofileowner.LOCK_TASK_CREATE";
+    public static final String DESTROY_ACTION = "com.android.cts.deviceandprofileowner.LOCK_TASK_DESTROY";
+    public static final String PAUSE_ACTION = "com.android.cts.deviceandprofileowner.LOCK_TASK_PAUSE";
+    public static final String RESUME_ACTION = "com.android.cts.deviceandprofileowner.LOCK_TASK_RESUME";
+    public static final String INTENT_ACTION = "com.android.cts.deviceandprofileowner.LOCK_TASK_INTENT";
+
+    private static volatile boolean isActivityResumed;
+    private static final Object ACTIVITY_RESUMED_LOCK = new Object();
+
+    private static void setIsActivityResumed(boolean newValue) {
+        synchronized (ACTIVITY_RESUMED_LOCK) {
+            isActivityResumed = newValue;
+            ACTIVITY_RESUMED_LOCK.notify();
+        }
+    }
+
+    /** Returns true if it's successful. */
+    public static boolean waitUntilActivityResumed(long timeoutMs) throws InterruptedException {
+        synchronized (ACTIVITY_RESUMED_LOCK) {
+            if (!isActivityResumed) {
+                ACTIVITY_RESUMED_LOCK.wait(timeoutMs);
+            }
+            return isActivityResumed;
+        }
+    }
+
+    /** Returns true if it's successful. */
+    public static boolean waitUntilActivityPaused(long timeoutMs) throws InterruptedException {
+        synchronized (ACTIVITY_RESUMED_LOCK) {
+            if (isActivityResumed) {
+                ACTIVITY_RESUMED_LOCK.wait(timeoutMs);
+            }
+            return !isActivityResumed;
+        }
+    }
 
     @Override
     protected void onNewIntent(Intent intent) {
@@ -55,12 +85,14 @@
 
     @Override
     protected void onResume() {
+        setIsActivityResumed(true);
         sendLocalBroadcast(new Intent(RESUME_ACTION));
         super.onResume();
     }
 
     @Override
     protected void onPause() {
+        setIsActivityResumed(false);
         sendLocalBroadcast(new Intent(PAUSE_ACTION));
         super.onPause();
     }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivityIfWhitelisted.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivityIfWhitelisted.java
similarity index 93%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivityIfWhitelisted.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivityIfWhitelisted.java
index 4cf6efa..d030fdd 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskUtilityActivityIfWhitelisted.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskUtilityActivityIfWhitelisted.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.cts.deviceowner;
+package com.android.cts.deviceandprofileowner;
 
 public class LockTaskUtilityActivityIfWhitelisted extends LockTaskUtilityActivity {
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index ebb848b..da569f3 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -72,19 +72,6 @@
         </service>
 
         <activity
-            android:name="com.android.cts.deviceowner.LockTaskUtilityActivity" />
-        <activity
-            android:name="com.android.cts.deviceowner.LockTaskUtilityActivityIfWhitelisted"
-            android:launchMode="singleInstance"
-            android:lockTaskMode="if_whitelisted">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.HOME"/>
-                <category android:name="android.intent.category.DEFAULT"/>
-            </intent-filter>
-        </activity>
-
-        <activity
             android:name=".SetPolicyActivity"
             android:launchMode="singleTop">
             <intent-filter>
@@ -95,6 +82,13 @@
 
         <activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"/>
 
+        <service android:name="com.android.cts.deviceowner.NotificationListener"
+                 android:label="Notification Listener"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
index 42fb4464..b793408 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
@@ -15,9 +15,8 @@
  */
 package com.android.cts.deviceowner;
 
-import android.app.admin.DevicePolicyManager;
+import android.app.PendingIntent;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.os.Process;
 import android.provider.Settings;
 
@@ -26,6 +25,7 @@
 import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
 public class AdminActionBookkeepingTest extends BaseDeviceOwnerTest {
     /*
@@ -107,6 +107,10 @@
      * Test: Requesting a bug report should update the corresponding timestamp.
      */
     public void testRequestBugreport() throws Exception {
+        // This test leaves a notification which will block future tests that request bug reports
+        // to fix this - we dismiss the bug report before returning
+        CountDownLatch notificationDismissedLatch = initTestRequestBugreport();
+
         Thread.sleep(1);
         final long previousTimestamp = mDevicePolicyManager.getLastBugReportRequestTime();
 
@@ -118,6 +122,34 @@
         assertTrue(newTimestamp > previousTimestamp);
         assertTrue(newTimestamp >= timeBefore);
         assertTrue(newTimestamp <= timeAfter);
+
+        cleanupTestRequestBugreport(notificationDismissedLatch);
+    }
+
+    private CountDownLatch initTestRequestBugreport() {
+        CountDownLatch notificationDismissedLatch = new CountDownLatch(1);
+        NotificationListener.getInstance().addListener((sbt) -> {
+            // The notification we are looking for is the one which confirms the bug report is
+            // ready and asks for consent to send it
+            if (sbt.getPackageName().equals("android") &&
+                    sbt.getTag().equals("DevicePolicyManager") &&
+                    sbt.getNotification().actions != null) {
+                try {
+                    // The first action is to decline
+                    sbt.getNotification().actions[0].actionIntent.send();
+                    notificationDismissedLatch.countDown();
+                } catch (PendingIntent.CanceledException e) {
+                    fail("Could not dismiss bug report notification");
+                }
+            }
+        });
+        return notificationDismissedLatch;
+    }
+
+    private void cleanupTestRequestBugreport(CountDownLatch notificationDismissedLatch)
+            throws Exception {
+        notificationDismissedLatch.await();
+        NotificationListener.getInstance().clearListeners();
     }
 
     /**
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
index 57d200b..2424c46 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AffiliationTest.java
@@ -17,12 +17,8 @@
 package com.android.cts.deviceowner;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
-import static org.junit.Assert.assertArrayEquals;
-
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -81,52 +77,8 @@
         setAffiliationIds(Collections.singleton("id.number.2"));
     }
 
-    @Test
-    public void testLockTaskMethodsThrowExceptionIfUnaffiliated() {
-        checkLockTaskMethodsThrow();
-    }
-
-    /** Assumes that the calling user is already affiliated before calling this method */
-    @Test
-    public void testSetLockTaskPackagesClearedIfUserBecomesUnaffiliated() {
-        final String[] packages = {"package1", "package2"};
-        mDevicePolicyManager.setLockTaskPackages(mAdminComponent, packages);
-        assertArrayEquals(packages, mDevicePolicyManager.getLockTaskPackages(mAdminComponent));
-        assertTrue(mDevicePolicyManager.isLockTaskPermitted("package1"));
-        assertFalse(mDevicePolicyManager.isLockTaskPermitted("package3"));
-
-        final Set<String> previousAffiliationIds =
-                mDevicePolicyManager.getAffiliationIds(mAdminComponent);
-        try {
-            // Clearing affiliation ids for this user. Lock task methods unavailable.
-            setAffiliationIds(Collections.emptySet());
-            checkLockTaskMethodsThrow();
-            assertFalse(mDevicePolicyManager.isLockTaskPermitted("package1"));
-
-            // Affiliating the user again. Previously set packages have been cleared.
-            setAffiliationIds(previousAffiliationIds);
-            assertEquals(0, mDevicePolicyManager.getLockTaskPackages(mAdminComponent).length);
-            assertFalse(mDevicePolicyManager.isLockTaskPermitted("package1"));
-        } finally {
-            mDevicePolicyManager.setAffiliationIds(mAdminComponent, previousAffiliationIds);
-        }
-    }
-
     private void setAffiliationIds(Set<String> ids) {
         mDevicePolicyManager.setAffiliationIds(mAdminComponent, ids);
         assertEquals(ids, mDevicePolicyManager.getAffiliationIds(mAdminComponent));
     }
-
-    private void checkLockTaskMethodsThrow() {
-        try {
-            mDevicePolicyManager.setLockTaskPackages(mAdminComponent, new String[0]);
-            fail("setLockTaskPackages did not throw expected SecurityException");
-        } catch (SecurityException expected) {
-        }
-        try {
-            mDevicePolicyManager.getLockTaskPackages(mAdminComponent);
-            fail("getLockTaskPackages did not throw expected SecurityException");
-        } catch (SecurityException expected) {
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
index 1cbf5b2..b69a60c 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/InstallUpdateTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.deviceowner;
 
+import static android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID;
+
 import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -54,38 +56,38 @@
     }
 
     public void testInstallUpdate_failNoZipOtaFile() throws InterruptedException {
-        assertUpdateError("notZip.zi",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        if (!isDeviceAB()) {
+            return;
+        }
+        assertUpdateError("notZip.zi", UPDATE_ERROR_UPDATE_FILE_INVALID);
     }
 
     public void testInstallUpdate_failWrongPayloadFile() throws InterruptedException {
-        assertUpdateError("wrongPayload.zip",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        if (!isDeviceAB()) {
+            return;
+        }
+        assertUpdateError("wrongPayload.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
     }
 
     public void testInstallUpdate_failEmptyOtaFile() throws InterruptedException {
-        assertUpdateError("empty.zip",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        if (!isDeviceAB()) {
+            return;
+        }
+        assertUpdateError("empty.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
     }
 
     public void testInstallUpdate_failWrongHash() throws InterruptedException {
-        assertUpdateError("wrongHash.zip",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        if (!isDeviceAB()) {
+            return;
+        }
+        assertUpdateError("wrongHash.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
     }
 
     public void testInstallUpdate_failWrongSize() throws InterruptedException {
-        assertUpdateError("wrongSize.zip",
-                isDeviceAB()
-                        ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                        : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        if (!isDeviceAB()) {
+            return;
+        }
+        assertUpdateError("wrongSize.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
     }
 
     public void testInstallUpdate_notCharging_belowThreshold_failsBatteryCheck() throws Exception {
@@ -101,15 +103,15 @@
     }
 
     public void testInstallUpdate_notCharging_aboveThreshold_passesBatteryCheck() throws Exception {
+        if (!isDeviceAB()) {
+            return;
+        }
         try {
             setNonChargingBatteryThreshold(TEST_BATTERY_THRESHOLD);
             setNonChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD);
             // Positive CTS tests aren't possible, so we verify that we get the file-related error
             // rather than the battery one.
-            assertUpdateError("wrongSize.zip",
-                    isDeviceAB()
-                            ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                            : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+            assertUpdateError("wrongSize.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
         } finally {
             resetBatteryState();
             resetDevicePolicyConstants();
@@ -129,15 +131,15 @@
     }
 
     public void testInstallUpdate_charging_aboveThreshold_passesBatteryCheck() throws Exception {
+        if (!isDeviceAB()) {
+            return;
+        }
         try {
             setChargingBatteryThreshold(TEST_BATTERY_THRESHOLD);
             setChargingBatteryLevelAndWait(TEST_BATTERY_THRESHOLD);
             // Positive CTS tests aren't possible, so we verify that we get the file-related error
             // rather than the battery one.
-            assertUpdateError("wrongSize.zip",
-                    isDeviceAB()
-                            ? InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID
-                            : InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
+            assertUpdateError("wrongSize.zip", UPDATE_ERROR_UPDATE_FILE_INVALID);
         } finally {
             resetBatteryState();
             resetDevicePolicyConstants();
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
deleted file mode 100644
index 99b97bd..0000000
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.deviceowner;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import android.app.ActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.support.test.uiautomator.UiDevice;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class that is meant to be driven from the host and can't be run alone, which is required
- * for tests that include rebooting or other connection-breaking steps. For this reason, this class
- * does not override tearDown and setUp just initializes the test state, changing nothing in the
- * device. Therefore, the host is responsible for making sure the tests leave the device in a clean
- * state after running.
- */
-@RunWith(AndroidJUnit4.class)
-public class LockTaskHostDrivenTest {
-
-    private static final String TAG = LockTaskHostDrivenTest.class.getName();
-
-    private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 20000;  // 20 seconds
-
-    private static final String LOCK_TASK_ACTIVITY
-            = LockTaskUtilityActivityIfWhitelisted.class.getName();
-
-    private UiDevice mUiDevice;
-    private Context mContext;
-    private ActivityManager mActivityManager;
-    private DevicePolicyManager mDevicePolicyManager;
-
-    private volatile boolean mIsActivityResumed;
-    private final Object mActivityResumedLock = new Object();
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            Log.d(TAG, "onReceive: " + action);
-            if (LockTaskUtilityActivity.RESUME_ACTION.equals(action)) {
-                synchronized (mActivityResumedLock) {
-                    mIsActivityResumed = true;
-                    mActivityResumedLock.notify();
-                }
-            } else if (LockTaskUtilityActivity.PAUSE_ACTION.equals(action)) {
-                synchronized (mActivityResumedLock) {
-                    mIsActivityResumed = false;
-                    mActivityResumedLock.notify();
-                }
-            }
-        }
-    };
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
-        mActivityManager = mContext.getSystemService(ActivityManager.class);
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(LockTaskUtilityActivity.RESUME_ACTION);
-        filter.addAction(LockTaskUtilityActivity.PAUSE_ACTION);
-        mContext.registerReceiver(mReceiver, filter);
-    }
-
-    @After
-    public void tearDown() {
-        mContext.unregisterReceiver(mReceiver);
-    }
-  
-    @Test
-    public void startLockTask() throws Exception {
-        Log.d(TAG, "startLockTask on host-driven test (no cleanup)");
-        setDefaultHomeIntentReceiver();
-        launchLockTaskActivity();
-        mUiDevice.waitForIdle();
-    }
-
-    /**
-     * On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
-     * assume it's finished. Therefore, only use it once in a given test.
-     */
-    @Test
-    public void testLockTaskIsActiveAndCantBeInterrupted() throws Exception {
-        mUiDevice.waitForIdle();
-
-        // We need to wait until the LockTaskActivity is ready
-        // since com.android.cts.deviceowner can be killed by AMS for reason "start instr".
-        synchronized (mActivityResumedLock) {
-            if (!mIsActivityResumed) {
-                mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
-            }
-        }
-        checkLockedActivityIsRunning();
-
-        mUiDevice.pressBack();
-        mUiDevice.waitForIdle();
-        checkLockedActivityIsRunning();
-
-        mUiDevice.pressHome();
-        mUiDevice.waitForIdle();
-        checkLockedActivityIsRunning();
-
-        mUiDevice.pressRecentApps();
-        mUiDevice.waitForIdle();
-        checkLockedActivityIsRunning();
-
-        mUiDevice.waitForIdle();
-    }
-
-    @Test
-    public void clearDefaultHomeIntentReceiver() {
-        mDevicePolicyManager.clearPackagePersistentPreferredActivities(
-                BasicAdminReceiver.getComponentName(mContext),
-                mContext.getPackageName());
-        mDevicePolicyManager.setLockTaskPackages(BasicAdminReceiver.getComponentName(mContext),
-                new String[0]);
-    }
-
-    private void checkLockedActivityIsRunning() throws Exception {
-        assertTrue(isActivityOnTop());
-        assertEquals(ActivityManager.LOCK_TASK_MODE_LOCKED,
-                mActivityManager.getLockTaskModeState());
-    }
-
-    private boolean isActivityOnTop() {
-        return mActivityManager.getAppTasks().get(0).getTaskInfo().topActivity
-                .getClassName().equals(LOCK_TASK_ACTIVITY);
-    }
-
-    private void launchLockTaskActivity() {
-        Intent intent = new Intent(mContext, LockTaskUtilityActivityIfWhitelisted.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(LockTaskUtilityActivity.START_LOCK_TASK, true);
-        mContext.startActivity(intent);
-    }
-
-    private void setDefaultHomeIntentReceiver() {
-        mDevicePolicyManager.setLockTaskPackages(BasicAdminReceiver.getComponentName(mContext),
-                new String[]{mContext.getPackageName()});
-        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MAIN);
-        intentFilter.addCategory(Intent.CATEGORY_HOME);
-        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
-        mDevicePolicyManager.addPersistentPreferredActivity(
-                BasicAdminReceiver.getComponentName(mContext), intentFilter,
-                new ComponentName(mContext.getPackageName(), LOCK_TASK_ACTIVITY));
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NotificationListener.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NotificationListener.java
new file mode 100644
index 0000000..f224221
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NotificationListener.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.deviceowner;
+
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * A {@link NotificationListenerService} that allows tests to register to respond to notifications.
+ */
+public class NotificationListener extends NotificationListenerService {
+
+    private static final int TIMEOUT_SECONDS = 120;
+
+    private static NotificationListener instance;
+    private static CountDownLatch connectedLatch = new CountDownLatch(1);
+
+    public static NotificationListener getInstance() {
+        try {
+            connectedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException("NotificationListener not connected.", e);
+        }
+
+        return instance;
+    }
+
+    private List<Consumer<StatusBarNotification>> mListeners = new ArrayList<>();
+
+    public void addListener(Consumer<StatusBarNotification> listener) {
+        mListeners.add(listener);
+    }
+
+    public void clearListeners() {
+        mListeners.clear();
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn) {
+        for (Consumer<StatusBarNotification> listener : mListeners) {
+            listener.accept(sbn);
+        }
+    }
+
+    @Override
+    public void onListenerConnected() {
+        instance = this;
+        connectedLatch.countDown();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
index 8f1f88e..c91a86a 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.managedprofile;
 
 import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -33,7 +34,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Verify that certain cross profile intent filters are disallowed when the device admin sets
@@ -176,7 +176,7 @@
                         UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
             }
             // Wait for the restriction to apply
-            assertTrue("Restriction not applied after 5 seconds", latch.await(5, TimeUnit.SECONDS));
+            assertTrue("Restriction not applied after 30 seconds", latch.await(30, SECONDS));
         } finally {
             mContext.unregisterReceiver(receiver);
         }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 3b0cbfb..8d94e90 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -136,6 +136,9 @@
     /** Whether the device has a lock screen.*/
     protected boolean mHasSecureLockScreen;
 
+    /** Whether the device supports telephony. */
+    protected boolean mHasTelephony;
+
     /** Users we shouldn't delete in the tests */
     private ArrayList<Integer> mFixedUsers;
 
@@ -156,6 +159,7 @@
         }
         mSupportsMultiUser = getMaxNumberOfUsersSupported() > 1;
         mSupportsFbe = hasDeviceFeature("android.software.file_based_encryption");
+        mHasTelephony = hasDeviceFeature("android.hardware.telephony");
         mFixedPackages = getDevice().getInstalledPackageNames();
         mBuildHelper = new CompatibilityBuildHelper(mCtsBuild);
 
@@ -185,6 +189,7 @@
         }
 
         removeOwners();
+        switchUser(USER_SYSTEM);
         removeTestUsers();
         // Unlock keyguard before test
         wakeupAndDismissKeyguard();
@@ -199,6 +204,7 @@
         getDevice().executeShellCommand("settings put global package_verifier_enable "
                 + mPackageVerifier);
         removeOwners();
+        switchUser(USER_SYSTEM);
         removeTestUsers();
         removeTestPackages();
         super.tearDown();
@@ -236,6 +242,16 @@
     }
 
     /**
+     * Initializes the user with the given id, and waits until the user has started and unlocked
+     * before continuing.
+     *
+     * <p>This is required so that apps can run on it.
+     */
+    protected void startUser(int userId, boolean waitFlag) throws Exception {
+        getDevice().startUser(userId, waitFlag);
+    }
+
+    /**
      * Starts switching to the user with the given ID.
      *
      * <p>This is not blocking. Some operations will be flaky if called immediately afterwards, such
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 56014ea..ceafda1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -50,14 +50,16 @@
 
     public static final String DEVICE_ADMIN_PKG = "com.android.cts.deviceandprofileowner";
     public static final String DEVICE_ADMIN_APK = "CtsDeviceAndProfileOwnerApp.apk";
-    public static final String ADMIN_RECEIVER_TEST_CLASS
+    protected static final String ADMIN_RECEIVER_TEST_CLASS
             = ".BaseDeviceAdminTest$BasicAdminReceiver";
+    protected static final String DEVICE_ADMIN_COMPONENT_FLATTENED =
+            DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS;
 
     private static final String STORAGE_ENCRYPTION_TEST_CLASS = ".StorageEncryptionTest";
     private static final String IS_PRIMARY_USER_PARAM = "isPrimaryUser";
 
-    private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
-    private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+    protected static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
+    protected static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
     private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
     private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
@@ -1056,6 +1058,111 @@
                     .build());
     }
 
+    public void testLockTask() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+            executeDeviceTestClass(".LockTaskTest");
+            assertMetricsLogged(
+                    getDevice(),
+                    () -> executeDeviceTestMethod(".LockTaskTest", "testStartLockTask"),
+                    new DevicePolicyEventWrapper.Builder(EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
+                            .setAdminPackageName(DEVICE_ADMIN_PKG)
+                            .setBoolean(true)
+                            .setStrings(DEVICE_ADMIN_PKG)
+                            .build());
+        } catch (AssertionError ex) {
+            // STOPSHIP(b/32771855), remove this once we fixed the bug.
+            executeShellCommand("dumpsys activity activities");
+            executeShellCommand("dumpsys window -a");
+            executeShellCommand("dumpsys activity service com.android.systemui");
+            throw ex;
+        } finally {
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+        }
+    }
+
+    public void testLockTaskAfterReboot() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            // Just start kiosk mode
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "startLockTask");
+
+            // Reboot while in kiosk mode and then unlock the device
+            rebootAndWaitUntilReady();
+
+            // Check that kiosk mode is working and can't be interrupted
+            executeDeviceTestMethod(".LockTaskHostDrivenTest",
+                    "testLockTaskIsActiveAndCantBeInterrupted");
+        } finally {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+        }
+    }
+
+    public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            // Just start kiosk mode
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "startLockTask");
+
+            // Reboot while in kiosk mode and then unlock the device
+            rebootAndWaitUntilReady();
+
+            // Try to open settings via adb
+            executeShellCommand("am start -a android.settings.SETTINGS");
+
+            // Check again
+            executeDeviceTestMethod(".LockTaskHostDrivenTest",
+                    "testLockTaskIsActiveAndCantBeInterrupted");
+        } finally {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+        }
+    }
+
+    public void testLockTask_defaultDialer() throws Exception {
+        if (!mHasFeature || !mHasTelephony) {
+            return;
+        }
+        try {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest",
+                    "testLockTaskCanLaunchDefaultDialer");
+        } finally {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+        }
+    }
+
+    public void testLockTask_emergencyDialer() throws Exception {
+        if (!mHasFeature || !mHasTelephony) {
+            return;
+        }
+        try {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest",
+                    "testLockTaskCanLaunchEmergencyDialer");
+        } finally {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+        }
+    }
+
+    public void testLockTask_exitIfNoLongerWhitelisted() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest",
+                    "testLockTaskIsExitedIfNotWhitelisted");
+        } finally {
+            executeDeviceTestMethod(".LockTaskHostDrivenTest", "cleanupLockTask");
+        }
+    }
+
     public void testSuspendPackage() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
index 0b7cc6b..712cb50 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
@@ -592,7 +592,7 @@
             final int userId = createManagedProfile(mPrimaryUserId);
             installAppAsUser(apkName, userId);
             setProfileOwnerOrFail(adminReceiverClassName, userId);
-            startUser(userId);
+            startUser(userId, /* waitFlag= */ true);
             runDeviceTestsAsUser(
                     packageName,
                     MANAGEMENT_TEST,
@@ -617,7 +617,7 @@
         List<Integer> newUsers = getUsersCreatedByTests();
         assertEquals(1, newUsers.size());
         int secondaryUserId = newUsers.get(0);
-        getDevice().startUser(secondaryUserId);
+        getDevice().startUser(secondaryUserId, /* waitFlag= */ true);
         return secondaryUserId;
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index a42799e..746bd64 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
+import android.stats.devicepolicy.EventId;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -33,8 +35,6 @@
 import java.util.List;
 import java.util.Map;
 
-import android.stats.devicepolicy.EventId;
-
 /**
  * Set of tests for Device Owner use cases.
  */
@@ -77,16 +77,9 @@
 
     /**
      * Copied from {@link android.app.admin.DevicePolicyManager
-     * .InstallSystemUpdateCallback#UPDATE_ERROR_UPDATE_FILE_INVALID}
+     * .InstallSystemUpdateCallback#UPDATE_ERROR_BATTERY_LOW}
      */
-    private static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
-
-    /**
-     * Copied from {@link android.app.admin.DevicePolicyManager
-     * .InstallSystemUpdateCallback#UPDATE_ERROR_UNKNOWN}
-     */
-    private static final int UPDATE_ERROR_UNKNOWN = 1;
-
+    private static final int UPDATE_ERROR_BATTERY_LOW = 5;
     private static final int TYPE_NONE = 0;
 
     /**
@@ -119,6 +112,8 @@
             }
 
             getDevice().executeShellCommand(" mkdir " + TEST_UPDATE_LOCATION);
+            // Enable the notification listener
+            getDevice().executeShellCommand("cmd notification allow_listener com.android.cts.deviceowner/com.android.cts.deviceowner.NotificationListener");
         }
         mHasCreateAndManageUserFeature = mHasFeature && canCreateAdditionalUsers(1)
                 && hasDeviceFeature("android.software.managed_users");
@@ -282,15 +277,24 @@
             return;
         }
 
+        int maxUsers = getDevice().getMaxNumberOfUsersSupported();
         int maxRunningUsers = getDevice().getMaxNumberOfRunningUsersSupported();
-        // Primary user is already running, so we can start up to maxRunningUsers -1.
-        for (int i = 0; i < maxRunningUsers - 1; i++) {
+
+        // Primary user is already running, so we can create and start up to minimum of above - 1.
+        int usersToCreateAndStart = Math.min(maxUsers, maxRunningUsers) - 1;
+        for (int i = 0; i < usersToCreateAndStart; i++) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
                     "testCreateAndManageUser_StartInBackground");
         }
-        // The next startUserInBackground should return USER_OPERATION_ERROR_MAX_RUNNING_USERS.
-        executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testCreateAndManageUser_StartInBackground_MaxRunningUsers");
+
+        if (maxUsers > maxRunningUsers) {
+            // The next startUserInBackground should return USER_OPERATION_ERROR_MAX_RUNNING_USERS.
+            executeDeviceTestMethod(".CreateAndManageUserTest",
+                    "testCreateAndManageUser_StartInBackground_MaxRunningUsers");
+        } else {
+            // The next createAndManageUser should return USER_OPERATION_ERROR_MAX_USERS.
+            executeDeviceTestMethod(".CreateAndManageUserTest", "testCreateAndManageUser_MaxUsers");
+        }
     }
 
     /**
@@ -568,113 +572,6 @@
         executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
     }
 
-    public void testLockTask_deviceOwnerUser() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        try {
-            installAppAsUser(INTENT_RECEIVER_APK, mPrimaryUserId);
-            executeDeviceOwnerTest("LockTaskTest");
-            assertMetricsLogged(getDevice(), () -> {
-                runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskTest", "testStartLockTask",
-                        mPrimaryUserId);
-            }, new DevicePolicyEventWrapper.Builder(EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
-                    .setAdminPackageName(DEVICE_OWNER_PKG)
-                    .setBoolean(true)
-                    .setStrings(DEVICE_OWNER_PKG)
-                    .build());
-        } catch (AssertionError ex) {
-            // STOPSHIP(b/32771855), remove this once we fixed the bug.
-            executeShellCommand("dumpsys activity activities");
-            executeShellCommand("dumpsys window -a");
-            executeShellCommand("dumpsys activity service com.android.systemui");
-            throw ex;
-        } finally {
-            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
-        }
-    }
-
-    public void testLockTaskAfterReboot_deviceOwnerUser() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        try {
-            // Just start kiosk mode
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest", "startLockTask",
-                    mPrimaryUserId);
-
-            // Reboot while in kiosk mode and then unlock the device
-            rebootAndWaitUntilReady();
-
-            // Check that kiosk mode is working and can't be interrupted
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest",
-                    "testLockTaskIsActiveAndCantBeInterrupted", mPrimaryUserId);
-        } finally {
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest",
-                    "clearDefaultHomeIntentReceiver", mPrimaryUserId);
-        }
-    }
-
-    public void testLockTaskAfterReboot_tryOpeningSettings_deviceOwnerUser() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        try {
-            // Just start kiosk mode
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest", "startLockTask",
-                    mPrimaryUserId);
-
-            // Reboot while in kiosk mode and then unlock the device
-            rebootAndWaitUntilReady();
-
-            // Try to open settings via adb
-            executeShellCommand("am start -a android.settings.SETTINGS");
-
-            // Check again
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest",
-                    "testLockTaskIsActiveAndCantBeInterrupted", mPrimaryUserId);
-        } finally {
-            runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest",
-                    "clearDefaultHomeIntentReceiver", mPrimaryUserId);
-        }
-    }
-
-    public void testLockTask_unaffiliatedUser() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
-            return;
-        }
-
-        final int userId = createUser();
-        installAppAsUser(DEVICE_OWNER_APK, userId);
-        setProfileOwnerOrFail(DEVICE_OWNER_COMPONENT, userId);
-
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG,
-                ".AffiliationTest",
-                "testLockTaskMethodsThrowExceptionIfUnaffiliated",
-                userId);
-
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", mPrimaryUserId);
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", userId);
-        runDeviceTestsAsUser(
-                DEVICE_OWNER_PKG,
-                ".AffiliationTest",
-                "testSetLockTaskPackagesClearedIfUserBecomesUnaffiliated",
-                userId);
-    }
-
-    public void testLockTask_affiliatedSecondaryUser() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
-            return;
-        }
-        final int userId = createAffiliatedSecondaryUser();
-        executeAffiliatedProfileOwnerTest("LockTaskTest", userId);
-    }
-
     public void testSystemUpdatePolicy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -813,23 +710,11 @@
             new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_PRE_REBOOT_SECURITY_LOGS_VALUE)
                     .setAdminPackageName(DEVICE_OWNER_PKG)
                     .build());
-
-        // Requesting a bug report (in AdminActionBookkeepingTest#testRequestBugreport) leaves a
-        // state where future bug report requests will fail
-        // TODO(b/130210665): replace this with use of NotificationListenerService to dismiss the
-        // bug report request
-        rebootAndWaitUntilReady();
-
         assertMetricsLogged(getDevice(), () -> {
             executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRequestBugreport");
         }, new DevicePolicyEventWrapper.Builder(EventId.REQUEST_BUGREPORT_VALUE)
                 .setAdminPackageName(DEVICE_OWNER_PKG)
                 .build());
-        // Requesting a bug report (in AdminActionBookkeepingTest#testRequestBugreport) leaves a
-        // state where future bug report requests will fail
-        // TODO(b/130210665): replace this with use of NotificationListenerService to dismiss the
-        // bug report request
-        rebootAndWaitUntilReady();
     }
 
     public void testBluetoothRestriction() throws Exception {
@@ -1044,18 +929,17 @@
         if (!mHasFeature) {
             return;
         }
-        pushUpdateFileToDevice("wrongHash.zip");
+        pushUpdateFileToDevice("wrongSize.zip");
         assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".InstallUpdateTest", "testInstallUpdate_failWrongHash");
+                executeDeviceTestMethod(".InstallUpdateTest",
+                        "testInstallUpdate_notCharging_belowThreshold_failsBatteryCheck");
         }, new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_VALUE)
-                    .setAdminPackageName(DEVICE_OWNER_PKG)
-                    .setBoolean(isDeviceAb())
-                    .build(),
-            new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_ERROR_VALUE)
-                    .setInt(isDeviceAb()
-                            ? UPDATE_ERROR_UPDATE_FILE_INVALID
-                            : UPDATE_ERROR_UNKNOWN)
-                    .build());
+               .setAdminPackageName(DEVICE_OWNER_PKG)
+               .setBoolean(isDeviceAb())
+               .build(),
+        new DevicePolicyEventWrapper.Builder(EventId.INSTALL_SYSTEM_UPDATE_ERROR_VALUE)
+               .setInt(UPDATE_ERROR_BATTERY_LOW)
+               .build());
     }
 
     private boolean isDeviceAb() throws DeviceNotAvailableException {
@@ -1135,15 +1019,6 @@
         runDeviceTestsAsUser(DEVICE_OWNER_PKG, testClass, mPrimaryUserId);
     }
 
-    private void executeAffiliatedProfileOwnerTest(String testClassName, int userId)
-            throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        String testClass = DEVICE_OWNER_PKG + "." + testClassName;
-        runDeviceTestsAsUser(DEVICE_OWNER_PKG, testClass, userId);
-    }
-
     private void executeDeviceTestMethod(String className, String testName) throws Exception {
         if (!mHasFeature) {
             return;
@@ -1162,7 +1037,7 @@
         waitForBroadcastIdle();
         wakeupAndDismissKeyguard();
 
-        // Setting the same affiliation ids on both users and running the lock task tests.
+        // Setting the same affiliation ids on both users
         runDeviceTestsAsUser(
                 DEVICE_OWNER_PKG, ".AffiliationTest", "testSetAffiliationId1", mPrimaryUserId);
         runDeviceTestsAsUser(
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 1dc783e..6f3c07c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -16,16 +16,11 @@
 
 package com.android.cts.devicepolicy;
 
-import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
-
 import android.stats.devicepolicy.EventId;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 
-import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -46,10 +41,9 @@
             mUserId = mPrimaryUserId;
 
             installAppAsUser(DEVICE_ADMIN_APK, mUserId);
-            if (!setDeviceOwner(
-                    DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
-                    /*expectFailure*/ false)) {
-                removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+            if (!setDeviceOwner(DEVICE_ADMIN_COMPONENT_FLATTENED, mUserId, /*expectFailure*/
+                    false)) {
+                removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mUserId);
                 getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
                 fail("Failed to set device owner");
             }
@@ -60,11 +54,39 @@
     protected void tearDown() throws Exception {
         if (mHasFeature) {
             assertTrue("Failed to remove device owner",
-                    removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId));
+                    removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mUserId));
         }
         super.tearDown();
     }
 
+    public void testLockTask_unaffiliatedUser() throws Exception {
+        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        final int userId = createSecondaryUserAsProfileOwner();
+        runDeviceTestsAsUser(
+                DEVICE_ADMIN_PKG, ".AffiliationTest",
+                "testLockTaskMethodsThrowExceptionIfUnaffiliated", userId);
+
+        setUserAsAffiliatedUserToPrimary(userId);
+        runDeviceTestsAsUser(
+                DEVICE_ADMIN_PKG,
+                ".AffiliationTest",
+                "testSetLockTaskPackagesClearedIfUserBecomesUnaffiliated",
+                userId);
+    }
+
+    public void testLockTask_affiliatedSecondaryUser() throws Exception {
+        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+        final int userId = createSecondaryUserAsProfileOwner();
+        switchToUser(userId);
+        setUserAsAffiliatedUserToPrimary(userId);
+        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".LockTaskTest", userId);
+    }
+
     public void testDelegatedCertInstallerDeviceIdAttestation() throws Exception {
         if (!mHasFeature) {
             return;
@@ -105,4 +127,26 @@
         result.add(DELEGATION_NETWORK_LOGGING);
         return result;
     }
+
+    private int createSecondaryUserAsProfileOwner() throws Exception {
+        final int userId = createUser();
+        installAppAsUser(INTENT_RECEIVER_APK, userId);
+        installAppAsUser(DEVICE_ADMIN_APK, userId);
+        setProfileOwnerOrFail(DEVICE_ADMIN_COMPONENT_FLATTENED, userId);
+        return userId;
+    }
+
+    private void switchToUser(int userId) throws Exception {
+        switchUser(userId);
+        waitForBroadcastIdle();
+        wakeupAndDismissKeyguard();
+    }
+
+    private void setUserAsAffiliatedUserToPrimary(int userId) throws Exception {
+        // Setting the same affiliation ids on both users
+        runDeviceTestsAsUser(
+                DEVICE_ADMIN_PKG, ".AffiliationTest", "testSetAffiliationId1", mPrimaryUserId);
+        runDeviceTestsAsUser(
+                DEVICE_ADMIN_PKG, ".AffiliationTest", "testSetAffiliationId1", userId);
+    }
 }
diff --git a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
index 625839f..ae8d4a8 100644
--- a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
+++ b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
@@ -21,6 +21,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
 import android.media.MediaCodec;
+import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.cts.bitstreams.MediaBitstreams;
@@ -83,6 +84,35 @@
         }
     }
 
+    private static void fixFormat(MediaFormat format, String path) {
+        // TODO(b/137684344): Revisit so that we can get this information from
+        //                    the bitstream or the extractor.
+        if (path.indexOf("/10bit/") < 0) {
+            return;
+        }
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        int profile = -1, level = -1;
+        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+            profile = CodecProfileLevel.VP9Profile2;
+            level = CodecProfileLevel.VP9Level1;
+        } else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            profile = CodecProfileLevel.HEVCProfileMain10;
+            level = CodecProfileLevel.HEVCMainTierLevel1;
+        } else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
+            profile = CodecProfileLevel.AV1ProfileMain10;
+            level = CodecProfileLevel.AV1Level2;
+        } else {
+            return;
+        }
+
+        if (!format.containsKey(MediaFormat.KEY_PROFILE)) {
+            format.setInteger(MediaFormat.KEY_PROFILE, profile);
+        }
+        if (!format.containsKey(MediaFormat.KEY_LEVEL)) {
+            format.setInteger(MediaFormat.KEY_LEVEL, level);
+        }
+    }
+
     static interface ReportCallback {
         void run(OutputStream out) throws Exception;
     }
@@ -166,6 +196,7 @@
                 MediaFormat format = parseTrackFormat(formatStr);
                 String mime = format.getString(MediaFormat.KEY_MIME);
                 String[] decoders = MediaUtils.getDecoderNamesForMime(mime);
+                fixFormat(format, path);
 
                 ps.println(path);
                 ps.println(decoders.length);
@@ -225,6 +256,7 @@
             try {
                 ex.setDataSource(path);
                 MediaFormat format = ex.getTrackFormat(0);
+                fixFormat(format, path);
                 boolean[] vendors = new boolean[] {false, true};
                 for (boolean v : vendors) {
                     for (String name : MediaUtils.getDecoderNames(v, format)) {
diff --git a/hostsidetests/securitybulletin/securityPatch/pac/Android.mk b/hostsidetests/securitybulletin/securityPatch/pac/Android.mk
new file mode 100644
index 0000000..bd9c07d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/pac/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := pacrunner
+LOCAL_SRC_FILES := pac.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES:= \
+        $(TOP)/external/chromium-libpac/includes \
+
+LOCAL_SHARED_LIBRARIES := \
+        libpac \
+
+LOCAL_COMPATIBILITY_SUITE := cts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp b/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp
new file mode 100644
index 0000000..393848d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <proxy_resolver_v8_wrapper.h>
+#include <sys/types.h>
+#include <string.h>
+#include <codecvt>
+#include <fstream>
+#include <iostream>
+
+const char16_t* spec = u"";
+const char16_t* host = u"";
+
+int main(int argc, char *argv[]) {
+  if (argc != 2) {
+    std::cout << "incorrect number of arguments" << std::endl;
+    std::cout << "usage: ./pacrunner mypac.pac" << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  ProxyResolverV8Handle* handle = ProxyResolverV8Handle_new();
+
+  std::ifstream t;
+  t.open(argv[1]);
+  if (t.rdstate() != std::ifstream::goodbit) {
+    std::cout << "error opening file" << std::endl;
+    return EXIT_FAILURE;
+  }
+  t.seekg(0, std::ios::end);
+  size_t size = t.tellg();
+  // allocate an extra byte for the null terminator
+  char* raw = (char*)calloc(size + 1, sizeof(char));
+  t.seekg(0);
+  t.read(raw, size);
+  std::string u8Script(raw);
+  std::u16string u16Script = std::wstring_convert<
+        std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(u8Script);
+
+  ProxyResolverV8Handle_SetPacScript(handle, u16Script.data());
+  ProxyResolverV8Handle_GetProxyForURL(handle, spec, host);
+
+  ProxyResolverV8Handle_delete(handle);
+  return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 25b27e8..cb66496 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -1104,8 +1104,9 @@
         try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
                 "action.show_notification")) {
             Thread.sleep(WAIT_TIME_SHORT);
-            // Trigger new pull.
+            // Trigger a pull and wait for new pull before killing the process.
             setAppBreadcrumbPredicate();
+            Thread.sleep(WAIT_TIME_LONG);
         }
 
         // Assert about ProcessMemoryState for the test app.
@@ -1145,6 +1146,7 @@
 
         // Trigger new pull.
         setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_LONG);
 
         // Assert about NativeProcessMemoryState for statsd.
         List<Atom> atoms = getGaugeMetricDataList();
@@ -1181,8 +1183,8 @@
         try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
                 "action.show_notification")) {
             setAppBreadcrumbPredicate();
+            Thread.sleep(WAIT_TIME_LONG);
         }
-        Thread.sleep(WAIT_TIME_SHORT);
 
         // Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server.
         List<Atom> atoms = getGaugeMetricDataList();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
index 5c45080..c809f26 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
@@ -23,6 +23,8 @@
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import androidx.test.filters.FlakyTest;
+
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -85,6 +87,7 @@
     }
 
     @MediumTest
+    @FlakyTest
     public void testPerformGlobalActionNotifications() throws Exception {
         // Perform the action under test
         assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
@@ -102,6 +105,7 @@
     }
 
     @MediumTest
+    @FlakyTest
     public void testPerformGlobalActionQuickSettings() throws Exception {
         // Check whether the action succeeded.
         assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
@@ -122,6 +126,7 @@
     }
 
     @MediumTest
+    @FlakyTest
     public void testPerformGlobalActionPowerDialog() throws Exception {
         // Check whether the action succeeded.
         assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 697fe18..6e77fea 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -342,6 +342,11 @@
     private void cancelAndPoll(int id) {
         mNotificationManager.cancel(id);
 
+        try {
+            Thread.sleep(500);
+        } catch (InterruptedException ex) {
+            // pass
+        }
         if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
             fail("canceled notification was still alive, id=" + id);
         }
@@ -2626,8 +2631,10 @@
             // Should be foreground now
             a.sendBubble(1);
 
+            boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
+
             if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
-                    true /* shouldExist */, true /* shouldBeBubble */)) {
+                    true /* shouldExist */, shouldBeBubble)) {
                 fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
             }
 
@@ -2638,8 +2645,6 @@
             // The notif should be allowed to update as a bubble
             a.sendBubble(2);
 
-            boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
-
             if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
                     true /* shouldExist */, shouldBeBubble)) {
                 fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
diff --git a/tests/autofillservice/OWNERS b/tests/autofillservice/OWNERS
index d924ccc..eac063b 100644
--- a/tests/autofillservice/OWNERS
+++ b/tests/autofillservice/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 351486
 felipeal@google.com
+svetoslavganov@google.com
+adamhe@google.com
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index a955587..6884f06 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -173,4 +173,11 @@
         }
         super.finish();
     }
+
+    /**
+     * Clears focus from input fields.
+     */
+    public void clearFocus() {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
index b9d631e..6e7b475 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
@@ -60,7 +60,7 @@
         try {
             event = mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
                     Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
-        } catch (TimeoutException ex) {
+        } catch (WindowChangeTimeoutException ex) {
             // no window events! looking good
             return;
         }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
index c0b2994..e7c0cf7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
@@ -55,7 +55,7 @@
         try {
             event = mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
                     Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
-        } catch (TimeoutException ex) {
+        } catch (WindowChangeTimeoutException ex) {
             // no window events! looking good
             return;
         }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 0338851..691ee97 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -196,15 +196,15 @@
         @ClassRule
         public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule();
 
+        protected static final RequiredFeatureRule sRequiredFeatureRule =
+                new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
+
         private final TestWatcher mTestWatcher = new AutofillTestWatcher();
 
         private final RetryRule mRetryRule = new RetryRule(getNumberRetries());
 
         private final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
 
-        private final RequiredFeatureRule mRequiredFeatureRule =
-                new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
-
         protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
                 .setDumper(mLoggingRule)
                 .run(() -> sReplier.assertNoUnhandledFillRequests())
@@ -214,8 +214,8 @@
         @Rule
         public final RuleChain mLookAllTheseRules = RuleChain
                 //
-                // mRequiredFeatureRule should be first so the test can be skipped right away
-                .outerRule(mRequiredFeatureRule)
+                // requiredFeatureRule should be first so the test can be skipped right away
+                .outerRule(getRequiredFeaturesRule())
                 //
                 // mTestWatcher should always be one the first rules, as it defines the name of the
                 // test being ran and finishes dangling activities at the end
@@ -288,6 +288,17 @@
         }
 
         /**
+         * Gets a rule that defines which features must be present for this test to run.
+         *
+         * <p>By default it returns a rule that requires {@link PackageManager#FEATURE_AUTOFILL},
+         * but subclass can override to be more specific.
+         */
+        @NonNull
+        protected TestRule getRequiredFeaturesRule() {
+            return sRequiredFeatureRule;
+        }
+
+        /**
          * Gets the test-specific {@link Rule @Rule}.
          *
          * <p>Sub-class <b>MUST</b> override this method instead of annotation their own rules,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index cabdee3..4aa0858 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -305,6 +305,9 @@
         // Autofill it.
         mUiBot.selectDataset("dataset");
 
+        // TODO(b/137856201): Fix race condition in getSelectedItemPosition().
+        Thread.sleep(1000);
+
         if (expectAutoFill) {
             // Check the results.
             spinnerWatcher.assertAutoFilled();
@@ -524,7 +527,7 @@
 
     @Test
     public void autofillInvalidListValueToRadioGroup() throws Exception {
-        autofillListValue(AutofillValue.forList(-1), 0, false);
+        autofillRadioGroup(AutofillValue.forList(-1), 0, false);
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index 75b92ec..efd001f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -232,9 +232,7 @@
         syncRunOnUiThread(() -> v.visit(mUsernameEditText));
     }
 
-    /**
-     * Clears focus from input fields by focusing on the parent layout.
-     */
+    @Override
     public void clearFocus() {
         syncRunOnUiThread(() -> ((View) mUsernameContainer.getParent()).requestFocus());
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 2e56a02..bcc1bc0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -894,19 +894,24 @@
      * Execute a Runnable and wait for {@link AccessibilityEvent#TYPE_WINDOWS_CHANGED} or
      * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
      */
-    public AccessibilityEvent waitForWindowChange(Runnable runnable, long timeoutMillis)
-            throws TimeoutException {
-        return mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
-            switch (event.getEventType()) {
-                case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
-                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-                    return true;
-            }
-            return false;
-        }, timeoutMillis);
+    public AccessibilityEvent waitForWindowChange(Runnable runnable, long timeoutMillis) {
+        try {
+            return mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
+                switch (event.getEventType()) {
+                    case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
+                    case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+                        return true;
+                    default:
+                        Log.v(TAG, "waitForWindowChange(): ignoring event " + event);
+                }
+                return false;
+            }, timeoutMillis);
+        } catch (TimeoutException e) {
+            throw new WindowChangeTimeoutException(e, timeoutMillis);
+        }
     }
 
-    public AccessibilityEvent waitForWindowChange(Runnable runnable) throws TimeoutException {
+    public AccessibilityEvent waitForWindowChange(Runnable runnable) {
         return waitForWindowChange(runnable, Timeouts.WINDOW_CHANGE_TIMEOUT_MS);
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index 430a533..3253f29 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
@@ -167,4 +167,9 @@
     public UiObject2 getLoginButton() throws Exception {
         return mLoginButton;
     }
+
+    @Override
+    public void clearFocus() {
+        syncRunOnUiThread(() -> mParent.requestFocus());
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index ca68a4e..3af85fb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -353,16 +353,19 @@
         Helper.assertTextIsSanitized(outside2FillNode);
 
         // Move focus around to make sure UI is shown accordingly
+        mActivity.clearFocus();
         mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
         callback.assertUiHiddenEvent(myWebView, usernameChildId);
         mUiBot.assertDatasets("OUT1");
         callback.assertUiShownEvent(mActivity.mOutside1);
 
+        mActivity.clearFocus();
         mActivity.getPasswordInput().click();
         callback.assertUiHiddenEvent(mActivity.mOutside1);
         mUiBot.assertDatasets("PASS");
         final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
 
+        mActivity.clearFocus();
         mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
         callback.assertUiHiddenEvent(myWebView, passwordChildId);
         final UiObject2 datasetPicker = mUiBot.assertDatasets("OUT2");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WindowChangeTimeoutException.java b/tests/autofillservice/src/android/autofillservice/cts/WindowChangeTimeoutException.java
new file mode 100644
index 0000000..1225a47
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/WindowChangeTimeoutException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.autofillservice.cts;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.RetryableException;
+
+public final class WindowChangeTimeoutException extends RetryableException {
+
+    public WindowChangeTimeoutException(@NonNull Throwable cause, long timeoutMillis) {
+        super(cause, "no window change event in %dms", timeoutMillis);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillAutoActivityLaunchTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillAutoActivityLaunchTestCase.java
index 88c92ed..023a5cd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillAutoActivityLaunchTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillAutoActivityLaunchTestCase.java
@@ -24,9 +24,13 @@
 import android.content.AutofillOptions;
 import android.view.autofill.AutofillManager;
 
+import com.android.compatibility.common.util.RequiredSystemResourceRule;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 
 /////
 ///// NOTE: changes in this class should also be applied to
@@ -42,6 +46,13 @@
 
     private CtsAugmentedAutofillService.ServiceWatcher mServiceWatcher;
 
+    private static final RequiredSystemResourceRule sRequiredResource =
+            new RequiredSystemResourceRule("config_defaultAugmentedAutofillService");
+
+    private static final RuleChain sRequiredFeatures = RuleChain
+            .outerRule(sRequiredFeatureRule)
+            .around(sRequiredResource);
+
     @BeforeClass
     public static void allowAugmentedAutofill() {
         sContext.getApplicationContext()
@@ -83,6 +94,11 @@
         return AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
     }
 
+    @Override
+    protected TestRule getRequiredFeaturesRule() {
+        return sRequiredFeatures;
+    }
+
     protected CtsAugmentedAutofillService enableAugmentedService() throws InterruptedException {
         if (mServiceWatcher != null) {
             throw new IllegalStateException("There Can Be Only One!");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillManualActivityLaunchTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillManualActivityLaunchTestCase.java
index aa65877..32e6b88 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillManualActivityLaunchTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAutofillManualActivityLaunchTestCase.java
@@ -23,9 +23,13 @@
 import android.content.AutofillOptions;
 import android.view.autofill.AutofillManager;
 
+import com.android.compatibility.common.util.RequiredSystemResourceRule;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 
 /////
 ///// NOTE: changes in this class should also be applied to
@@ -41,6 +45,13 @@
 
     private CtsAugmentedAutofillService.ServiceWatcher mServiceWatcher;
 
+    private static final RequiredSystemResourceRule sRequiredResource =
+            new RequiredSystemResourceRule("config_defaultAugmentedAutofillService");
+
+    private static final RuleChain sRequiredFeatures = RuleChain
+            .outerRule(sRequiredFeatureRule)
+            .around(sRequiredResource);
+
     @BeforeClass
     public static void allowAugmentedAutofill() {
         sContext.getApplicationContext()
@@ -78,6 +89,11 @@
         return AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
     }
 
+    @Override
+    protected TestRule getRequiredFeaturesRule() {
+        return sRequiredFeatures;
+    }
+
     protected CtsAugmentedAutofillService enableAugmentedService() throws InterruptedException {
         return enableAugmentedService(/* whitelistSelf= */ true);
     }
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 6893703..b9fc256 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -784,7 +784,10 @@
                 break;
         }
 
-        ACameraMetadata_getConstEntry(mChars, streamConfigTag, &entry);
+        auto ret = ACameraMetadata_getConstEntry(mChars, streamConfigTag, &entry);
+        if (ret != ACAMERA_OK) {
+            return false;
+        }
         for (uint32_t i = 0; i < entry.count; i += 4) {
             if (entry.data.i32[i] == format &&
                     entry.data.i32[i+3] == streamConfigOutputTag &&
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index c845568..2dbe878 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -1485,67 +1485,11 @@
         // TODO: jchowdhary@, b/130323585, this line can be removed.
         requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
         CaptureRequest flashOffRequest = requestBuilder.build();
-        int flashModeOffRequests = captureRequestsSynchronized(flashOffRequest, 2, listener,
-                mHandler);
-        // We check that the Nth frame's capture result has flash state as either PARTIAL / READY
-        // and the N+1th frame has its flash state as READY (to take into account flash off ramp
-        // time). For devices supporting per frame control, N is the 1st frame passed into
-        // captureRequestsSynchronized. No two frames can have their flash state as PARTIAL.
-        CaptureResult firstTorchOffResult = null;
-        CaptureResult secondTorchOffResult = null;
-        String torchOffDebugStr = "Timed out waiting for torch off result";
-        if (mStaticInfo.isPerFrameControlSupported()) {
-            firstTorchOffResult =
-                    listener.getCaptureResultForRequest(flashOffRequest, NUM_RESULTS_WAIT_TIMEOUT);
-            secondTorchOffResult =
-                    listener.getCaptureResultForRequest(flashOffRequest, NUM_RESULTS_WAIT_TIMEOUT);
-
-        } else {
-            CaptureResult maybeFirstNonFiredResult;
-            int i = 0;
-            while (i < flashModeOffRequests - 1) {
-                maybeFirstNonFiredResult = listener.getCaptureResultForRequest(flashOffRequest,
-                        NUM_RESULTS_WAIT_TIMEOUT);
-                i++;
-                Integer flashState = maybeFirstNonFiredResult.get(CaptureResult.FLASH_STATE);
-                torchOffDebugStr = "Flash not turned off in time";
-                if (flashState != CaptureResult.FLASH_STATE_FIRED) {
-                    firstTorchOffResult = maybeFirstNonFiredResult;
-                    secondTorchOffResult = listener.getCaptureResultForRequest(flashOffRequest,
-                            NUM_RESULTS_WAIT_TIMEOUT);
-                    torchOffDebugStr = "Timed out waiting for torch off result";
-                    break;
-                }
-            }
-            // The next results should all have their flash state as READY
-            for(int j = i + 1 ; j < flashModeOffRequests; j++) {
-                CaptureResult expectedReadyResult =
-                        listener.getCaptureResultForRequest(flashOffRequest,
-                                NUM_RESULTS_WAIT_TIMEOUT);
-                mCollector.expectEquals("Flash state must remain READY after torch is turned off",
-                        CaptureResult.FLASH_STATE_READY,
-                        expectedReadyResult.get(CaptureResult.FLASH_STATE));
-            }
-        }
-        mCollector.expectNotEquals("first torch off result: " + torchOffDebugStr, null,
-                firstTorchOffResult);
-        mCollector.expectNotEquals("second torch off result: " + torchOffDebugStr, null,
-                secondTorchOffResult);
-        check2TorchOffStates(firstTorchOffResult, secondTorchOffResult);
-    }
-
-    private void check2TorchOffStates(CaptureResult first, CaptureResult second) {
-        Integer flashState = first.get(CaptureResult.FLASH_STATE);
-        // The first frame must have its flash state as FLASH_STATE_READY or
-        // FLASH_STATE_PARTIAL.
-        int [] validStates = new int[] { CaptureRequest.FLASH_STATE_READY,
-              CaptureRequest.FLASH_STATE_PARTIAL};
-        mCollector.expectContains("Flash state must be either PARTIAL or READY", validStates,
-                flashState);
-        // The next frame must have its flash state as FLASH_STATE_READY
-        flashState = second.get(CaptureResult.FLASH_STATE);
-        mCollector.expectEquals("Flash state result must be READY",
-                CaptureResult.FLASH_STATE_READY, flashState);
+        mSession.setRepeatingRequest(flashOffRequest, listener, mHandler);
+        waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_TORCH);
+        result = listener.getCaptureResultForRequest(flashOffRequest, NUM_RESULTS_WAIT_TIMEOUT);
+        mCollector.expectEquals("Flash state result must be READY", CaptureResult.FLASH_STATE_READY,
+                result.get(CaptureResult.FLASH_STATE));
     }
 
     /**
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 750e715..37dfbdd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -191,6 +191,7 @@
                 Size previewSize= findCommonPreviewSize(id, dualPhysicalCameraIds);
                 if (previewSize == null) {
                     Log.i(TAG, "Camera " + id + ": No matching physical preview streams, skipping");
+                    continue;
                 }
 
                 testBasicPhysicalStreamingForCamera(
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b7510e6..d108600e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -65,7 +65,7 @@
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final int CONFIGURE_TIMEOUT = 5000; //ms
-    private static final int CAPTURE_TIMEOUT = 1000; //ms
+    private static final int CAPTURE_TIMEOUT = 1500; //ms
 
     // For testTriggerInteractions
     private static final int PREVIEW_WARMUP_FRAMES = 60;
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index bd822f9..e8d2812 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -148,6 +148,39 @@
     }
 
     /**
+     * Test dynamic depth capture along with preview for each camera.
+     */
+    @Test
+    public void testDynamicDepthCapture() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing dynamic depth for Camera " + mCameraIds[i]);
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+                if (!mAllStaticInfo.get(mCameraIds[i]).isDepthJpegSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support dynamic depth, skipping");
+                    continue;
+                }
+
+                openDevice(mCameraIds[i]);
+
+                // Check the maximum supported size.
+                List<Size> orderedDepthJpegSizes = CameraTestUtils.getSortedSizesForFormat(
+                        mCameraIds[i], mCameraManager, ImageFormat.DEPTH_JPEG, null/*bound*/);
+                Size maxDepthJpegSize = orderedDepthJpegSizes.get(0);
+                stillDynamicDepthTestByCamera(ImageFormat.DEPTH_JPEG, maxDepthJpegSize);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
      * Test normal still capture sequence.
      * <p>
      * Preview and jpeg output streams are configured. Max still capture
@@ -1295,6 +1328,45 @@
         }
     }
 
+    /**
+     * Issue a still capture and validate the dynamic depth output.
+     */
+    private void stillDynamicDepthTestByCamera(int format, Size stillSize) throws Exception {
+        assertTrue(format == ImageFormat.DEPTH_JPEG);
+
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        if (VERBOSE) {
+            Log.v(TAG, "Testing dynamic depth with size " + stillSize.toString()
+                    + ", preview size " + maxPreviewSz);
+        }
+
+        // prepare capture and start preview.
+        CaptureRequest.Builder previewBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        prepareCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, stillSize,
+                ImageFormat.DEPTH_JPEG, resultListener, /*sessionListener*/null,
+                MAX_READER_IMAGES, imageListener);
+
+        // Capture a few dynamic depth images and check whether they are valid jpegs.
+        for (int i = 0; i < MAX_READER_IMAGES; i++) {
+            CaptureRequest request = stillBuilder.build();
+            mSession.capture(request, resultListener, mHandler);
+            CaptureResult stillResult =
+                resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            assertNotNull("Unable to acquire next image", image);
+            CameraTestUtils.validateImage(image, stillSize.getWidth(), stillSize.getHeight(),
+                    format, null /*filePath*/);
+
+            // Free image resources
+            image.close();
+        }
+    }
+
     private void aeCompensationTestByCamera() throws Exception {
         Range<Integer> compensationRange = mStaticInfo.getAeCompensationRangeChecked();
         // Skip the test if exposure compensation is not supported.
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 40d77f9..8069bdb 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -2393,6 +2393,14 @@
     }
 
     /**
+     * Check if Depth Jpeg format is supported
+     */
+    public boolean isDepthJpegSupported() {
+        int[] formats = getAvailableFormats(StaticMetadata.StreamDirection.Output);
+        return CameraTestUtils.contains(formats, ImageFormat.DEPTH_JPEG);
+    }
+
+    /**
      * Check if the dynamic black level is supported.
      *
      * <p>
diff --git a/tests/contentcaptureservice/AndroidManifest.xml b/tests/contentcaptureservice/AndroidManifest.xml
index 7f2c108..f0d262f 100644
--- a/tests/contentcaptureservice/AndroidManifest.xml
+++ b/tests/contentcaptureservice/AndroidManifest.xml
@@ -22,6 +22,49 @@
 
         <uses-library android:name="android.test.runner" />
 
+        <activity android:name=".BlankActivity"
+                  android:label="Blank"
+                  android:taskAffinity=".BlankActivity"
+                  android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".BlankWithTitleActivity"
+                  android:label="Blanka"
+                  android:taskAffinity=".BlankWithTitleActivity">
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".LoginActivity"
+                  android:label="Login"
+                  android:taskAffinity=".LoginActivity"
+                  android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".ChildlessActivity"
+                  android:label="Childless"
+                  android:taskAffinity=".ChildlessActivity"
+                  android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         <activity android:name=".CustomViewActivity"
                   android:label="CustomView"
                   android:taskAffinity=".CustomViewActivity"
diff --git a/tests/contentcaptureservice/res/layout/childless_activity.xml b/tests/contentcaptureservice/res/layout/childless_activity.xml
new file mode 100644
index 0000000..8cc67d5
--- /dev/null
+++ b/tests/contentcaptureservice/res/layout/childless_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/contentcaptureservice/res/layout/login_activity.xml b/tests/contentcaptureservice/res/layout/login_activity.xml
new file mode 100644
index 0000000..1164b4f
--- /dev/null
+++ b/tests/contentcaptureservice/res/layout/login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+
+      <TextView
+          android:id="@+id/username_label"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="Username" />
+
+      <EditText
+          android:id="@+id/username"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content" />
+
+      <TextView
+          android:id="@+id/password_label"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="Password" />
+
+      <EditText
+          android:id="@+id/password"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:inputType="textPassword" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index e7f2e68..8f77b2b 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -114,7 +114,7 @@
                 mServiceWatcher.waitOnDestroy();
             }
         } catch (Throwable t) {
-            Log.e(TAG, "error disablign service", t);
+            Log.e(TAG, "error disabling service", t);
         }
     }
 
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java
new file mode 100644
index 0000000..bd4fecc
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java
@@ -0,0 +1,121 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.DoubleVisitor;
+
+/**
+ * Base class for classes that have a {@code root_view} root view.
+ */
+abstract class AbstractRootViewActivity extends AbstractContentCaptureActivity {
+
+    private static final String TAG = AbstractRootViewActivity.class.getSimpleName();
+
+    private static DoubleVisitor<AbstractRootViewActivity, LinearLayout> sRootViewVisitor;
+    private static DoubleVisitor<AbstractRootViewActivity, LinearLayout> sOnAnimationVisitor;
+
+    private LinearLayout mRootView;
+
+    /**
+     * Sets a visitor called when the activity is created.
+     */
+    static void onRootView(@NonNull DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor) {
+        sRootViewVisitor = visitor;
+    }
+
+    /**
+     * Sets a visitor to be called on {@link Activity#onEnterAnimationComplete()}.
+     */
+    static void onAnimationComplete(
+            @NonNull DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor) {
+        sOnAnimationVisitor = visitor;
+    }
+
+    @Override
+    protected final void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentViewOnCreate(savedInstanceState);
+
+        mRootView = findViewById(R.id.root_view);
+
+        Log.d(TAG, "onCreate(): parents for " + getClass() + ": rootView=" + mRootView
+                + "\ngrandParent=" + getGrandParent()
+                + "\ngrandGrandParent=" + getGrandGrandParent());
+
+        if (sRootViewVisitor != null) {
+            Log.d(TAG, "Applying visitor to " + this + "/" + mRootView);
+            try {
+                sRootViewVisitor.visit(this, mRootView);
+            } finally {
+                sRootViewVisitor = null;
+            }
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Log.d(TAG, "AutofillIds for " + getClass() + ": "
+                + " rootView=" + getRootView().getAutofillId()
+                + ", grandParent=" + getGrandParent().getAutofillId()
+                + ", grandGrandParent=" + getGrandGrandParent().getAutofillId());
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        if (sOnAnimationVisitor != null) {
+            Log.i(TAG, "onEnterAnimationComplete(): applying visitor on " + this);
+            try {
+                sOnAnimationVisitor.visit(this, mRootView);
+            } finally {
+                sOnAnimationVisitor = null;
+            }
+        } else {
+            Log.i(TAG, "onEnterAnimationComplete(): no visitor on " + this);
+        }
+    }
+
+    public LinearLayout getRootView() {
+        return mRootView;
+    }
+
+    // TODO(b/122315042): remove this method when not needed anymore
+    @NonNull
+    public ViewGroup getGrandParent() {
+        return (ViewGroup) mRootView.getParent();
+    }
+
+    // TODO(b/122315042): remove this method when not needed anymore
+    @NonNull
+    public ViewGroup getGrandGrandParent() {
+        return (ViewGroup) getGrandParent().getParent();
+    }
+
+    /**
+     * The real "onCreate" method that should be extended by subclasses.
+     *
+     */
+    protected abstract void setContentViewOnCreate(Bundle savedInstanceState);
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
index 5203460..0f7e298 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
@@ -226,6 +226,22 @@
         assertSessionId(expectedSessionId, expectedView);
     }
 
+    /**
+     * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARING} event.
+     */
+    public static void assertViewTreeStarted(@NonNull List<ContentCaptureEvent> events,
+            int index) {
+        assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARING);
+    }
+
+    /**
+     * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARED} event.
+     */
+    public static void assertViewTreeFinished(@NonNull List<ContentCaptureEvent> events,
+            int index) {
+        assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARED);
+    }
+
     private static void assertSessionLevelEvent(@NonNull List<ContentCaptureEvent> events,
             int index, int expectedType) {
         final ContentCaptureEvent event = getEvent(events, index, expectedType);
diff --git a/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
similarity index 60%
rename from tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java
rename to tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
index 353ec45..ae9134f 100644
--- a/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
@@ -13,21 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.contentcaptureservice.cts;
 
-package android.app.role.cts.app;
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
 
-import android.app.Activity;
-import android.os.Bundle;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
 
-/**
- * An empty activity that finishes immediately.
- */
-public class EmptyActivity extends Activity {
+import androidx.annotation.NonNull;
+
+public class BlankActivity extends AbstractContentCaptureActivity {
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        finish();
+    public void assertDefaultEvents(@NonNull Session session) {
+        assertNoViewLevelEvents(session, this);
     }
 }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
new file mode 100644
index 0000000..6775d56
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.CtsContentCaptureService.CONTENT_CAPTURE_SERVICE_COMPONENT_NAME;
+import static android.contentcaptureservice.cts.Helper.resetService;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class BlankActivityTest
+        extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<BlankActivity> {
+
+    private static final String TAG = BlankActivityTest.class.getSimpleName();
+
+    private static final ActivityTestRule<BlankActivity> sActivityRule = new ActivityTestRule<>(
+            BlankActivity.class, false, false);
+
+    public BlankActivityTest() {
+        super(BlankActivity.class);
+    }
+
+    @Override
+    protected ActivityTestRule<BlankActivity> getActivityTestRule() {
+        return sActivityRule;
+    }
+
+    @Test
+    public void testSimpleSessionLifecycle() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        activity.assertDefaultEvents(session);
+    }
+
+    @Test
+    public void testGetServiceComponentName() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        service.waitUntilConnected();
+
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        try {
+            assertThat(activity.getContentCaptureManager().getServiceComponentName())
+                    .isEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+
+            resetService();
+            service.waitUntilDisconnected();
+
+            assertThat(activity.getContentCaptureManager().getServiceComponentName())
+                    .isNotEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+        } finally {
+            activity.finish();
+            watcher.waitFor(DESTROYED);
+        }
+    }
+
+    @Test
+    public void testGetServiceComponentName_onUiThread() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        service.waitUntilConnected();
+
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final AtomicReference<ComponentName> ref = new AtomicReference<>();
+        activity.syncRunOnUiThread(
+                () -> ref.set(activity.getContentCaptureManager().getServiceComponentName()));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        assertThat(ref.get()).isEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+    }
+
+    @Test
+    public void testIsContentCaptureFeatureEnabled_onUiThread() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        service.waitUntilConnected();
+
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final AtomicBoolean ref = new AtomicBoolean();
+        activity.syncRunOnUiThread(() -> ref
+                .set(activity.getContentCaptureManager().isContentCaptureFeatureEnabled()));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        assertThat(ref.get()).isTrue();
+    }
+
+    @Test
+    public void testDisableContentCaptureService_onUiThread() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        service.waitUntilConnected();
+
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        service.disableSelf();
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+    }
+
+    @Test
+    public void testOnConnectionEvents() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        service.waitUntilConnected();
+
+        resetService();
+        service.waitUntilDisconnected();
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
new file mode 100644
index 0000000..81198f7
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.util.Log;
+import android.view.View;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.ViewNode;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public class BlankWithTitleActivity extends AbstractContentCaptureActivity {
+
+    private static final String TAG = BlankWithTitleActivity.class.getSimpleName();
+
+    @Override
+    public void assertDefaultEvents(@NonNull Session session) {
+        final ContentCaptureSessionId sessionId = session.id;
+        assertRightActivity(session, sessionId, this);
+
+        final View decorView = getDecorView();
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+
+        final int minEvents = 9; // TODO(b/122315042): disappeared not always sent
+        assertThat(events.size()).isAtLeast(minEvents);
+
+        assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, decorView);
+        // TODO(b/123540067): ignoring 3 intermediate parents
+        final ViewNode title = assertViewAppeared(events, 6).getViewNode();
+        assertThat(title.getText()).isEqualTo("Blanka");
+        assertViewTreeFinished(events, 7);
+        assertSessionPaused(events, 8);
+        if (false) { // TODO(b/123540067): disabled because it includes the parent
+            assertViewsOptionallyDisappeared(events, minEvents, decorView.getAutofillId(),
+                    title.getAutofillId());
+        }
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java
new file mode 100644
index 0000000..c36e036
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.contentcaptureservice.cts;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import android.content.Intent;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+
+import org.junit.Test;
+
+public class BlankWithTitleActivityTest
+        extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<BlankWithTitleActivity> {
+
+    private static final String TAG = BlankWithTitleActivityTest.class.getSimpleName();
+
+    private static final ActivityTestRule<BlankWithTitleActivity> sActivityRule =
+            new ActivityTestRule<>(BlankWithTitleActivity.class, false, false);
+
+    public BlankWithTitleActivityTest() {
+        super(BlankWithTitleActivity.class);
+    }
+
+    @Override
+    protected ActivityTestRule<BlankWithTitleActivity> getActivityTestRule() {
+        return sActivityRule;
+    }
+
+    @Test
+    public void testSimpleSessionLifecycle() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankWithTitleActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        activity.assertDefaultEvents(session);
+    }
+
+    @AppModeFull(reason = "testSimpleSessionLifecycle() is enough")
+    @Test
+    public void testSimpleSessionLifecycle_noAnimation() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final BlankWithTitleActivity activity = launchActivity(
+                (intent) -> intent.addFlags(
+                        Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK));
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        activity.assertDefaultEvents(session);
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java
new file mode 100644
index 0000000..be6fd85
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java
@@ -0,0 +1,38 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+public class ChildlessActivity extends AbstractRootViewActivity {
+
+    @Override
+    protected void setContentViewOnCreate(Bundle savedInstanceState) {
+        setContentView(R.layout.childless_activity);
+    }
+
+    @Override
+    public void assertDefaultEvents(@NonNull Session session) {
+        // Should be empty because the root view is not important for content capture without a
+        // child that is important.
+        assertNoViewLevelEvents(session, this);
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
new file mode 100644
index 0000000..9f453b8
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
@@ -0,0 +1,1261 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.CREATION;
+import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.DESTRUCTION;
+import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertLifecycleOrder;
+import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewDisappeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsDisappeared;
+import static android.contentcaptureservice.cts.Helper.newImportantView;
+import static android.contentcaptureservice.cts.Helper.sContext;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.contentcaptureservice.cts.CtsContentCaptureService.DisconnectListener;
+import android.contentcaptureservice.cts.CtsContentCaptureService.ServiceWatcher;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.compatibility.common.util.ActivityLauncher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class ChildlessActivityTest
+        extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<ChildlessActivity> {
+
+    private static final String TAG = ChildlessActivityTest.class.getSimpleName();
+
+    private static final ActivityTestRule<ChildlessActivity> sActivityRule = new ActivityTestRule<>(
+            ChildlessActivity.class, false, false);
+
+    public ChildlessActivityTest() {
+        super(ChildlessActivity.class);
+    }
+
+    @Override
+    protected ActivityTestRule<ChildlessActivity> getActivityTestRule() {
+        return sActivityRule;
+    }
+
+    @Before
+    @After
+    public void resetActivityStaticState() {
+        ChildlessActivity.onRootView(null);
+    }
+
+    @Test
+    public void testDefaultLifecycle() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        activity.assertDefaultEvents(session);
+    }
+
+    @Test
+    public void testGetContentCapture_disabledWhenNoService() throws Exception {
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+    }
+
+    @Test
+    public void testGetContentCapture_enabledWhenNoService() throws Exception {
+        enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isTrue();
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+    }
+
+    @Test
+    public void testLaunchAnotherActivity() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher1 = startWatcher();
+
+        // Launch and finish 1st activity
+        final ChildlessActivity activity1 = launchActivity();
+        watcher1.waitFor(RESUMED);
+        activity1.finish();
+        watcher1.waitFor(DESTROYED);
+
+        // Launch and finish 2nd activity
+        final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>(
+                sContext, mActivitiesWatcher, LoginActivity.class);
+        final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher();
+        final LoginActivity activity2 = anotherActivityLauncher.launchActivity();
+        watcher2.waitFor(RESUMED);
+        activity2.finish();
+        watcher2.waitFor(DESTROYED);
+
+        // Assert the sessions
+        final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+        assertThat(sessionIds).hasSize(2);
+        final ContentCaptureSessionId sessionId1 = sessionIds.get(0);
+        Log.v(TAG, "session id1: " + sessionId1);
+        final ContentCaptureSessionId sessionId2 = sessionIds.get(1);
+        Log.v(TAG, "session id2: " + sessionId2);
+
+        final Session session1 = service.getFinishedSession(sessionId1);
+        activity1.assertDefaultEvents(session1);
+
+        final Session session2 = service.getFinishedSession(sessionId2);
+        activity2.assertDefaultEvents(session2);
+    }
+
+
+    @Test
+    public void testLaunchAnotherActivity_onTopOfIt() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher1 = startWatcher();
+
+        // Launch 1st activity
+        final ChildlessActivity activity1 = launchActivity();
+        watcher1.waitFor(RESUMED);
+
+        // Launch and finish 2nd activity
+        final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>(
+                sContext, mActivitiesWatcher, LoginActivity.class);
+        final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher();
+        final LoginActivity activity2 = anotherActivityLauncher.launchActivity();
+
+        watcher2.waitFor(RESUMED);
+        activity2.finish();
+        watcher2.waitFor(DESTROYED);
+
+        // Finish 1st activity
+        activity1.finish();
+        watcher1.waitFor(DESTROYED);
+
+        // Assert the activity lifecycle events
+        final ComponentName name1 = activity1.getComponentName();
+        final ComponentName name2 = activity2.getComponentName();
+        service.assertThat()
+            .activityResumed(name1)
+            .activityPaused(name1)
+            .activityResumed(name2)
+            .activityStopped(name1)
+            .activityPaused(name2)
+            .activityResumed(name1)
+            .activityDestroyed(name2)
+            .activityPaused(name1);
+
+        // Assert the sessions
+        final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+        assertThat(sessionIds).hasSize(2);
+        final ContentCaptureSessionId sessionId1 = sessionIds.get(0);
+        Log.v(TAG, "session id1: " + sessionId1);
+        final ContentCaptureSessionId sessionId2 = sessionIds.get(1);
+        Log.v(TAG, "session id2: " + sessionId2);
+
+        final Session session1 = service.getFinishedSession(sessionId1);
+        final List<ContentCaptureEvent> events1 = session1.getEvents();
+        Log.v(TAG, "events on " + activity1 + ": " + events1);
+        assertThat(events1).hasSize(4);
+        assertSessionResumed(events1, 0);
+        assertSessionPaused(events1, 1);
+        assertSessionResumed(events1, 2);
+        assertSessionPaused(events1, 3);
+
+        final Session session2 = service.getFinishedSession(sessionId2);
+        activity2.assertDefaultEvents(session2);
+
+    }
+
+    @Test
+    public void testAddAndRemoveNoImportantChild() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        // Child must be created inside the lambda because it needs to use the Activity context.
+        final AtomicReference<TextView> childRef = new AtomicReference<>();
+
+        ChildlessActivity.onRootView((activity, rootView) -> {
+            final TextView child = new TextView(activity);
+            child.setText("VIEW, Y U NO IMPORTANT?");
+            child.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+            rootView.addView(child);
+        });
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        // Remove view
+        final TextView child = childRef.get();
+        activity.syncRunOnUiThread(() -> activity.getRootView().removeView(child));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        // Should be empty because the root view is not important for content capture without a
+        // child that is important.
+        assertNoViewLevelEvents(session, activity);
+    }
+
+    @Test
+    public void testAddAndRemoveImportantChild() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        // TODO(b/120494182): Child must be created inside the lambda because it needs to use the
+        // Activity context.
+        final AtomicReference<TextView> childRef = new AtomicReference<>();
+
+        ChildlessActivity.onRootView((activity, rootView) -> {
+            final TextView text = newImportantView(activity, "Important I am");
+            rootView.addView(text);
+            childRef.set(text);
+        });
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        // Remove view
+        final LinearLayout rootView = activity.getRootView();
+        final TextView child = childRef.get();
+        activity.syncRunOnUiThread(() -> rootView.removeView(child));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+
+        final AutofillId rootId = rootView.getAutofillId();
+
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+        final View decorView = activity.getDecorView();
+
+        // Assert just the relevant events
+        assertThat(events.size()).isAtLeast(12);
+        assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, decorView);
+        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+        assertViewAppeared(events, 5, sessionId, rootView, grandpa1.getAutofillId());
+        assertViewAppeared(events, 6, sessionId, child, rootId);
+        assertViewTreeFinished(events, 7);
+        assertViewTreeStarted(events, 8);
+        assertViewDisappeared(events, 9, child.getAutofillId());
+        assertViewTreeFinished(events, 10);
+        assertSessionPaused(events, 11);
+
+        // TODO(b/122315042): assert parents disappeared
+    }
+
+    @Test
+    public void testAddImportantChildAfterSessionStarted() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        // Add View
+        final LinearLayout rootView = activity.getRootView();
+        final TextView child = newImportantView(activity, "Important I am");
+        activity.runOnUiThread(() -> rootView.addView(child));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+
+        final View grandpa = activity.getGrandParent();
+
+        // Assert just the relevant events
+
+        assertThat(events.size()).isAtLeast(6);
+        // TODO(b/122959591): figure out the child is coming first
+        assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertViewAppeared(events, 2, sessionId, child, rootView.getAutofillId());
+        assertViewAppeared(events, 3, sessionId, rootView, grandpa.getAutofillId());
+        assertViewTreeFinished(events, 4);
+        assertSessionPaused(events, 5);
+    }
+
+    @Test
+    public void testAddAndRemoveImportantChildOnDifferentSession() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final LinearLayout rootView = activity.getRootView();
+        final View grandpa = activity.getGrandParent();
+
+        final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        final ContentCaptureSession childSession = mainSession
+                .createContentCaptureSession(newContentCaptureContextBuilder("child")
+                        .build());
+        final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+        Log.v(TAG, "child session id: " + childSessionId);
+
+        final TextView child = newImportantView(activity, "Important I am");
+        final AutofillId childId = child.getAutofillId();
+        Log.v(TAG, "childId: " + childId);
+        child.setContentCaptureSession(childSession);
+        activity.runOnUiThread(() -> rootView.addView(child));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+        assertThat(sessionIds).containsExactly(mainSessionId, childSessionId).inOrder();
+
+        // Assert sessions
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+        Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+
+        assertThat(mainEvents.size()).isAtLeast(5);
+        assertSessionResumed(mainEvents, 0);
+        assertViewTreeStarted(mainEvents, 1);
+        assertViewAppeared(mainEvents, 2, mainSessionId, rootView, grandpa.getAutofillId());
+        assertViewTreeFinished(mainEvents, 3);
+        assertSessionPaused(mainEvents, 4);
+
+        final Session childTestSession = service.getFinishedSession(childSessionId);
+        assertChildSessionContext(childTestSession, "child");
+        final List<ContentCaptureEvent> childEvents = childTestSession.getEvents();
+        Log.v(TAG, "childEvents(" + childEvents.size() + "): " + childEvents);
+        final int minEvents = 3;
+        assertThat(childEvents.size()).isAtLeast(minEvents);
+        assertViewTreeStarted(childEvents, 0);
+        assertViewAppeared(childEvents, 1, childSessionId, child, rootView.getAutofillId());
+        assertViewTreeFinished(childEvents, 2);
+        // TODO(b/122315042): assert parents disappeared
+    }
+
+    /**
+     * Tests scenario where new sessions are added from the main session, but they're not nested
+     * neither have views attached to them.
+     */
+    @Test
+    public void testDinamicallyManageChildlessSiblingSessions() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create 1st session
+        final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+                .build();
+        final ContentCaptureSession childSession1 = mainSession
+                .createContentCaptureSession(context1);
+        final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 1: " + childSessionId1);
+
+        // Create 2nd session
+        final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+                .build();
+        final ContentCaptureSession childSession2 = mainSession
+                .createContentCaptureSession(context2);
+        final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 2: " + childSessionId2);
+
+        // Close 1st session before opening 3rd
+        childSession1.close();
+
+        // Create 3nd session...
+        final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+                .build();
+        final ContentCaptureSession childSession3 = mainSession
+                .createContentCaptureSession(context3);
+        final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 3: " + childSessionId3);
+
+        // ...and close it right away
+        childSession3.close();
+
+        // Create 4nd session
+        final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+                .build();
+        final ContentCaptureSession childSession4 = mainSession
+                .createContentCaptureSession(context4);
+        final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 4: " + childSessionId4);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(
+                mainSessionId,
+                childSessionId1,
+                childSessionId2,
+                childSessionId3,
+                childSessionId4)
+            .inOrder();
+
+        // Assert main sessions info
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+
+        final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+        assertChildSessionContext(childTestSession1, "session1");
+
+        final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+        assertChildSessionContext(childTestSession2, "session2");
+
+        final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+        assertChildSessionContext(childTestSession3, "session3");
+
+        final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+        assertChildSessionContext(childTestSession4, "session4");
+
+        // Gets all events first so they're all logged before the assertions
+        final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+        final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+        final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+        final List<ContentCaptureEvent> events3 = childTestSession3.getEvents();
+        final List<ContentCaptureEvent> events4 = childTestSession4.getEvents();
+        Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+        Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+        Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+        Log.v(TAG, "events3(" + events3.size() + "): " + events3);
+        Log.v(TAG, "events4(" + events4.size() + "): " + events4);
+
+        assertNoViewLevelEvents(mainTestSession, activity);
+        assertThat(events1).isEmpty();
+        assertThat(events2).isEmpty();
+        assertThat(events3).isEmpty();
+        assertThat(events4).isEmpty();
+
+        // Assert lifecycle methods were called in the right order
+        assertLifecycleOrder(1, mainTestSession,   CREATION);
+        assertLifecycleOrder(2, childTestSession1, CREATION);
+        assertLifecycleOrder(3, childTestSession2, CREATION);
+        assertLifecycleOrder(4, childTestSession1, DESTRUCTION);
+        assertLifecycleOrder(5, childTestSession3, CREATION);
+        assertLifecycleOrder(6, childTestSession3, DESTRUCTION);
+        assertLifecycleOrder(7, childTestSession4, CREATION);
+        assertLifecycleOrder(8, childTestSession2, DESTRUCTION);
+        assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+        assertLifecycleOrder(10, mainTestSession,  DESTRUCTION);
+    }
+
+    @Test
+    public void testDinamicallyAddOneChildOnAnotherSession_manuallyCloseSession() throws Exception {
+        dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ true);
+    }
+
+    @Test
+    public void testDinamicallyAddOneChildOnAnotherSession_autoCloseSession() throws Exception {
+        dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ false);
+    }
+
+    /**
+     * Tests scenario where just 1 session with 1 dinamically added view is created.
+     */
+    private void dinamicallyAddOneChildOnAnotherSessionTest(boolean manuallyCloseSession)
+            throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+        final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create session
+        final ContentCaptureSession childSession = mainSession
+                .createContentCaptureSession(
+                        newContentCaptureContextBuilder("child_session").build());
+        final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+        Log.v(TAG, "child session: " + childSessionId);
+
+        final TextView child = addChild(activity, childSession, "Sweet O'Mine");
+        if (manuallyCloseSession) {
+            waitAndClose(childSession);
+        }
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(mainSessionId, childSessionId).inOrder();
+
+        // Assert main session
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        // TODO(b/123540067): ideally it should be empty, but has intermediate parents stuff...
+        // assertThat(mainTestSession.getEvents()).isEmpty();
+
+        // Assert child session
+        final Session childTestSession = service.getFinishedSession(childSessionId);
+        assertChildSessionContext(childTestSession, "child_session");
+        final List<ContentCaptureEvent> childEvents = childTestSession.getEvents();
+        assertThat(childEvents.size()).isAtLeast(3);
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+        assertViewTreeStarted(childEvents, 0);
+        assertViewAppeared(childEvents, 1, child, rootId);
+        assertViewTreeFinished(childEvents, 2);
+
+        // Assert lifecycle methods were called in the right order
+        assertLifecycleOrder(1, mainTestSession,  CREATION);
+        assertLifecycleOrder(2, childTestSession, CREATION);
+        assertLifecycleOrder(3, childTestSession, DESTRUCTION);
+        assertLifecycleOrder(4, mainTestSession, DESTRUCTION);
+    }
+
+    /**
+     * Tests scenario where new sessions with children are added from the main session.
+     */
+    @Test
+    public void testDinamicallyManageSiblingSessions() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+        final LinearLayout rootView = activity.getRootView();
+        final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create 1st session
+        final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+                .build();
+        final ContentCaptureSession childSession1 = mainSession
+                .createContentCaptureSession(context1);
+        final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 1: " + childSessionId1);
+
+        // Session 1, child 1
+        final TextView s1c1 = addChild(activity, childSession1, "s1c1");
+
+        // Create 2nd session
+        final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+                .build();
+        final ContentCaptureSession childSession2 = mainSession
+                .createContentCaptureSession(context2);
+        final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 2: " + childSessionId2);
+
+        final TextView s2c1 = newImportantView(activity, childSession2, "s2c1");
+        final TextView s2c2 = newImportantView(activity, childSession2, "s2c1");
+
+        // Add 2 children together so they're wrapped a view_tree batch
+        activity.runOnUiThread(() -> {
+            rootView.addView(s2c1);
+            rootView.addView(s2c2);
+        });
+
+        // Close 1st session before opening 3rd
+        waitAndClose(childSession1);
+
+        // Create 3nd session...
+        final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+                .build();
+        final ContentCaptureSession childSession3 = mainSession
+                .createContentCaptureSession(context3);
+        final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 3: " + childSessionId3);
+
+        final TextView s3c1 = newImportantView(activity, childSession3, "s3c1");
+        final TextView s3c2 = newImportantView(activity, childSession3, "s3c1");
+        final TextView s3c3 = newImportantView(activity, childSession3, "s3c3");
+
+        // Add 2 children together so they're wrapped a view_tree batch
+        activity.runOnUiThread(() -> {
+            rootView.addView(s3c1);
+            rootView.addView(s3c2);
+        });
+
+        // TODO(b/123024698): need to wait until the 4 events are flushed - ideally we should block
+        // waiting until the service received them
+        sleep();
+
+        // Add 2 children so they're wrapped a view_tree batch
+        activity.runOnUiThread(() -> {
+            rootView.removeView(s3c1);
+            rootView.addView(s3c3);
+        });
+
+        // ...and close it right away
+        waitAndClose(childSession3);
+
+        // Create 4nd session
+        final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+                .build();
+        final ContentCaptureSession childSession4 = mainSession
+                .createContentCaptureSession(context4);
+        final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 4: " + childSessionId4);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(
+                mainSessionId,
+                childSessionId1,
+                childSessionId2,
+                childSessionId3,
+                childSessionId4)
+            .inOrder();
+
+        // Assert main sessions info
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+        Log.v(TAG, "main session events(" + mainEvents.size() + "): " + mainEvents);
+
+        // Gets all events first so they're all logged before the assertions
+        final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+        assertChildSessionContext(childTestSession1, "session1");
+        final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+        Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+
+        final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+        final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+        assertChildSessionContext(childTestSession2, "session2");
+        Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+        final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+        assertChildSessionContext(childTestSession3, "session3");
+        List<ContentCaptureEvent> events3 = childTestSession3.getEvents();
+        Log.v(TAG, "events3(" + events3.size() + "): " + events3);
+
+        final AutofillId rootId = rootView.getAutofillId();
+        final View grandpa = activity.getGrandParent();
+
+        assertThat(mainEvents).hasSize(8);
+        assertSessionResumed(mainEvents, 0);
+        assertViewTreeStarted(mainEvents, 1);
+        assertViewAppeared(mainEvents, 2, rootView, grandpa.getAutofillId());
+        assertViewTreeFinished(mainEvents, 3);
+        assertSessionPaused(mainEvents, 4); // TODO(b/122959591): investigate why
+        assertViewTreeStarted(mainEvents, 5);
+        assertViewDisappeared(mainEvents, 6, rootId);
+        assertViewTreeFinished(mainEvents, 7);
+
+        assertThat(events1).hasSize(3);
+        assertViewTreeStarted(events1, 0);
+        assertViewAppeared(events1, 1, s1c1, rootId);
+        assertViewTreeFinished(events1, 2);
+
+        assertThat(events2.size()).isAtLeast(4);
+        assertViewTreeStarted(events2, 0);
+        assertViewAppeared(events2, 1, s2c1, rootId);
+        assertViewAppeared(events2, 2, s2c2, rootId);
+        assertViewTreeFinished(events2, 3);
+        // TODO(b/122315042): assert parents disappeared
+
+        assertThat(events3).hasSize(8);
+        assertViewTreeStarted(events3, 0);
+        assertViewAppeared(events3, 1, s3c1, rootId);
+        assertViewAppeared(events3, 2, s3c2, rootId);
+        assertViewTreeFinished(events3, 3);
+        assertViewTreeStarted(events3, 4);
+        assertViewDisappeared(events3, 5, s3c1.getAutofillId());
+        assertViewAppeared(events3, 6, s3c3, rootId);
+        assertViewTreeFinished(events3, 7);
+
+        final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+        assertChildSessionContext(childTestSession4, "session4");
+        assertThat(childTestSession4.getEvents()).isEmpty();
+
+        // Assert lifecycle methods were called in the right order
+        assertLifecycleOrder(1, mainTestSession,   CREATION);
+        assertLifecycleOrder(2, childTestSession1, CREATION);
+        assertLifecycleOrder(3, childTestSession2, CREATION);
+        assertLifecycleOrder(4, childTestSession1, DESTRUCTION);
+        assertLifecycleOrder(5, childTestSession3, CREATION);
+        assertLifecycleOrder(6, childTestSession3, DESTRUCTION);
+        assertLifecycleOrder(7, childTestSession4, CREATION);
+        assertLifecycleOrder(8, childTestSession2, DESTRUCTION);
+        assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+        assertLifecycleOrder(10, mainTestSession,  DESTRUCTION);
+    }
+
+    @Test
+    public void testNestedSessions_simplestScenario() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create child session
+        final ContentCaptureContext childContext = newContentCaptureContextBuilder("child")
+                .build();
+        final ContentCaptureSession childSession = mainSession
+                .createContentCaptureSession(childContext);
+        final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+        Log.v(TAG, "child session id: " + childSessionId);
+
+        // Create grand child session
+        final ContentCaptureContext grandChild = newContentCaptureContextBuilder("grandChild")
+                .build();
+        final ContentCaptureSession grandChildSession = childSession
+                .createContentCaptureSession(grandChild);
+        final ContentCaptureSessionId grandChildSessionId = grandChildSession
+                .getContentCaptureSessionId();
+        Log.v(TAG, "child session id: " + grandChildSessionId);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(
+                mainSessionId,
+                childSessionId,
+                grandChildSessionId)
+            .inOrder();
+
+        // Assert sessions
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        assertNoViewLevelEvents(mainTestSession, activity);
+
+        final Session childTestSession = service.getFinishedSession(childSessionId);
+        assertChildSessionContext(childTestSession, "child");
+        assertThat(childTestSession.getEvents()).isEmpty();
+
+        final Session grandChildTestSession = service.getFinishedSession(grandChildSessionId);
+        assertChildSessionContext(grandChildTestSession, "grandChild");
+        assertThat(grandChildTestSession.getEvents()).isEmpty();
+
+        // Assert lifecycle methods were called in the right order
+        assertLifecycleOrder(1, mainTestSession, CREATION);
+        assertLifecycleOrder(2, childTestSession, CREATION);
+        assertLifecycleOrder(3, grandChildTestSession, CREATION);
+        assertLifecycleOrder(4, grandChildTestSession, DESTRUCTION);
+        assertLifecycleOrder(5, childTestSession, DESTRUCTION);
+        assertLifecycleOrder(6, mainTestSession,  DESTRUCTION);
+    }
+
+    /**
+     * Tests scenario where new sessions are added from each other session, but they're not nested
+     * neither have views attached to them.
+     *
+     * <p>This test actions are exactly the same as
+     * {@link #testDinamicallyManageChildlessSiblingSessions()}, except for session nesting (and
+     * order of lifecycle events).
+     */
+    @Test
+    public void testDinamicallyManageChildlessNestedSessions() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create 1st session
+        final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+                .build();
+        final ContentCaptureSession childSession1 = mainSession
+                .createContentCaptureSession(context1);
+        final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 1: " + childSessionId1);
+
+        // Create 2nd session
+        final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+                .build();
+        final ContentCaptureSession childSession2 = childSession1
+                .createContentCaptureSession(context2);
+        final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 2: " + childSessionId2);
+
+        // Close 1st session before opening 3rd
+        childSession1.close();
+
+        // Create 3nd session...
+        final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+                .build();
+        final ContentCaptureSession childSession3 = mainSession
+                .createContentCaptureSession(context3);
+        final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 3: " + childSessionId3);
+
+        // ...and close it right away
+        childSession3.close();
+
+        // Create 4nd session
+        final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+                .build();
+        final ContentCaptureSession childSession4 = mainSession
+                .createContentCaptureSession(context4);
+        final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 4: " + childSessionId4);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(
+                mainSessionId,
+                childSessionId1,
+                childSessionId2,
+                childSessionId3,
+                childSessionId4)
+            .inOrder();
+
+        // Assert main sessions info
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        assertNoViewLevelEvents(mainTestSession, activity);
+
+        final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+        assertChildSessionContext(childTestSession1, "session1");
+        assertThat(childTestSession1.getEvents()).isEmpty();
+
+        final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+        assertChildSessionContext(childTestSession2, "session2");
+        assertThat(childTestSession2.getEvents()).isEmpty();
+
+        final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+        assertChildSessionContext(childTestSession3, "session3");
+        assertThat(childTestSession3.getEvents()).isEmpty();
+
+        final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+        assertChildSessionContext(childTestSession4, "session4");
+        assertThat(childTestSession4.getEvents()).isEmpty();
+
+        // Assert lifecycle methods were called in the right order
+        assertLifecycleOrder(1, mainTestSession,   CREATION);
+        assertLifecycleOrder(2, childTestSession1, CREATION);
+        assertLifecycleOrder(3, childTestSession2, CREATION);
+        assertLifecycleOrder(4, childTestSession2, DESTRUCTION);
+        assertLifecycleOrder(5, childTestSession1, DESTRUCTION);
+        assertLifecycleOrder(6, childTestSession3, CREATION);
+        assertLifecycleOrder(7, childTestSession3, DESTRUCTION);
+        assertLifecycleOrder(8, childTestSession4, CREATION);
+        assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+        assertLifecycleOrder(10, mainTestSession,  DESTRUCTION);
+    }
+
+    /**
+     * Tests scenario where views from different session are removed in sequence - they should not
+     * have been batched.
+     */
+    @Test
+    public void testRemoveChildrenFromDifferentSessions() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ChildlessActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+        final LinearLayout rootView = activity.getRootView();
+        final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+        final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+        Log.v(TAG, "main session id: " + mainSessionId);
+
+        // Create 1st session
+        final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+                .build();
+        final ContentCaptureSession childSession1 = mainSession
+                .createContentCaptureSession(context1);
+        final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 1: " + childSessionId1);
+
+        // Session 1, child 1
+        final TextView s1c1 = addChild(activity, childSession1, "s1c1");
+        final AutofillId s1c1Id = s1c1.getAutofillId();
+        Log.v(TAG, "childrens from session1: " + s1c1Id);
+
+        // Create 2nd session
+        final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+                .build();
+        final ContentCaptureSession childSession2 = mainSession
+                .createContentCaptureSession(context2);
+        final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+        Log.v(TAG, "child session id 2: " + childSessionId2);
+
+        final TextView s2c1 = newImportantView(activity, childSession2, "s2c1");
+        final AutofillId s2c1Id = s2c1.getAutofillId();
+        final TextView s2c2 = newImportantView(activity, childSession2, "s2c2");
+        final AutofillId s2c2Id = s2c2.getAutofillId();
+        Log.v(TAG, "childrens from session2: " + s2c1Id + ", " + s2c2Id);
+
+        // Add 2 children together so they're wrapped a view_tree batch
+        activity.syncRunOnUiThread(() -> {
+            rootView.addView(s2c1);
+            rootView.addView(s2c2);
+        });
+
+        // Remove views - should generate one batch event for s2 and one single event for s1
+        waitAndRemoveViews(activity, s2c1, s2c2, s1c1);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+        assertThat(receivedIds).containsExactly(
+                mainSessionId,
+                childSessionId1,
+                childSessionId2)
+            .inOrder();
+
+        // Assert main sessions info
+        final Session mainTestSession = service.getFinishedSession(mainSessionId);
+        assertMainSessionContext(mainTestSession, activity);
+        final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+        Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+
+        // Logs events before asserting
+        final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+        assertChildSessionContext(childTestSession1, "session1");
+        final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+        Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+        final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+        final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+        assertChildSessionContext(childTestSession2, "session2");
+        Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+
+        // Assert children
+        assertThat(events1.size()).isAtLeast(6);
+        final AutofillId rootId = rootView.getAutofillId();
+        assertViewTreeStarted(events1, 0);
+        assertViewAppeared(events1, 1, s1c1, rootId);
+        assertViewTreeFinished(events1, 2);
+        assertViewTreeStarted(events1, 3);
+        assertViewDisappeared(events1, 4, s1c1Id);
+        assertViewTreeFinished(events1, 5);
+
+        assertThat(events2.size()).isAtLeast(7);
+        assertViewTreeStarted(events2, 0);
+        assertViewAppeared(events2, 1, s2c1, rootId);
+        assertViewAppeared(events2, 2, s2c2, rootId);
+        assertViewTreeFinished(events2, 3);
+        assertViewTreeStarted(events2, 4);
+        assertViewsDisappeared(events2, 5, s2c1Id, s2c2Id);
+        assertViewTreeFinished(events2, 6);
+    }
+
+    /* TODO(b/119638528): add more scenarios for nested sessions, such as:
+     * - add views to the children sessions
+     * - s1 -> s2 -> s3 and main -> s4; close(s1) then generate events on view from s3
+     * - s1 -> s2 -> s3 and main -> s4; close(s2) then generate events on view from s3
+     * - s1 -> s2 and s3->s4 -> s4
+     * - etc
+     */
+
+    private enum DisabledReason {
+        BY_API,
+        BY_SETTINGS,
+        BY_DEVICE_CONFIG
+    }
+
+    private void setFeatureEnabled(@NonNull CtsContentCaptureService service,
+            @NonNull DisabledReason reason,
+            boolean enabled) {
+        switch (reason) {
+            case BY_API:
+                if (enabled) {
+                    // The service cannot re-enable itself, so we use settings instead.
+                    setFeatureEnabledBySettings(true);
+                } else {
+                    service.disableSelf();
+                }
+                break;
+            case BY_SETTINGS:
+                setFeatureEnabledBySettings(enabled);
+                break;
+            case BY_DEVICE_CONFIG:
+                setFeatureEnabledByDeviceConfig(Boolean.toString(enabled));
+                break;
+            default:
+                throw new IllegalArgumentException("invalid reason: " + reason);
+        }
+    }
+
+    @Test
+    public void testIsContentCaptureFeatureEnabled_notService() throws Exception {
+        final ContentCaptureManager mgr = getContentCaptureManagerHack();
+        assertThrows(SecurityException.class,  () -> mgr.isContentCaptureFeatureEnabled());
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledBySettings() throws Exception {
+        setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_SETTINGS);
+    }
+
+    private void setContentCaptureFeatureEnabledTest_disabled(@NonNull DisabledReason reason)
+            throws Exception {
+        final ContentCaptureManager mgr = getContentCaptureManagerHack();
+
+        final CtsContentCaptureService service = enableService();
+        assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+        final DisconnectListener disconnectedListener = service.setOnDisconnectListener();
+
+        setFeatureEnabled(service, reason, /* enabled= */ false);
+
+        disconnectedListener.waitForOnDisconnected();
+        assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse();
+        assertThat(mgr.isContentCaptureEnabled()).isFalse();
+
+        final ActivityWatcher watcher = startWatcher();
+        final ChildlessActivity activity = launchActivity();
+
+        watcher.waitFor(RESUMED);
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        assertThat(service.getAllSessionIds()).isEmpty();
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledBySettings()
+            throws Exception {
+        setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_SETTINGS);
+    }
+
+    private void setContentCaptureFeatureEnabledTest_disabledThenReEnabled(
+            @NonNull DisabledReason reason) throws Exception {
+        final ContentCaptureManager mgr = getContentCaptureManagerHack();
+
+        final CtsContentCaptureService service1 = enableService();
+        assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+        final DisconnectListener disconnectedListener = service1.setOnDisconnectListener();
+
+        setFeatureEnabled(service1, reason, /* enabled= */ false);
+        disconnectedListener.waitForOnDisconnected();
+
+        assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse();
+        assertThat(mgr.isContentCaptureEnabled()).isFalse();
+
+        // Launch and finish 1st activity while it's disabled
+        final ActivityWatcher watcher1 = startWatcher();
+        final ChildlessActivity activity1 = launchActivity();
+        watcher1.waitFor(RESUMED);
+        activity1.finish();
+        watcher1.waitFor(DESTROYED);
+
+        // Re-enable feature
+        final ServiceWatcher reconnectionWatcher = CtsContentCaptureService.setServiceWatcher();
+        reconnectionWatcher.whitelistSelf();
+        setFeatureEnabled(service1, reason, /* enabled= */ true);
+        final CtsContentCaptureService service2 = reconnectionWatcher.waitOnCreate();
+        assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+
+        // Launch and finish 2nd activity while it's enabled
+        final ActivityLauncher<CustomViewActivity> launcher2 = new ActivityLauncher<>(
+                sContext, mActivitiesWatcher, CustomViewActivity.class);
+        final ActivityWatcher watcher2 = launcher2.getWatcher();
+        final CustomViewActivity activity2 = launcher2.launchActivity();
+        watcher2.waitFor(RESUMED);
+        activity2.finish();
+        watcher2.waitFor(DESTROYED);
+
+        assertThat(service1.getAllSessionIds()).isEmpty();
+        final Session session = service2.getOnlyFinishedSession();
+        activity2.assertDefaultEvents(session);
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledByApi() throws Exception {
+        setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_API);
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByApi()
+            throws Exception {
+        setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_API);
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledByDeviceConfig() throws Exception {
+        setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_DEVICE_CONFIG);
+        // Reset service, otherwise it will reconnect when the deviceConfig value is reset
+        // on cleanup, which will cause the test to fail
+        Helper.resetService();
+    }
+
+    @Test
+    public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByDeviceConfig()
+            throws Exception {
+        setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_DEVICE_CONFIG);
+        // Reset service, otherwise it will reconnect when the deviceConfig value is reset
+        // on cleanup, which will cause the test to fail
+        Helper.resetService();
+    }
+
+    // TODO(b/123406031): add tests that mix feature_enabled with user_restriction_enabled (and
+    // make sure mgr.isContentCaptureFeatureEnabled() returns only the state of the 1st)
+
+    private TextView addChild(@NonNull ChildlessActivity activity,
+            @NonNull ContentCaptureSession session, @NonNull String text) {
+        final TextView child = newImportantView(activity, text);
+        child.setContentCaptureSession(session);
+        Log.i(TAG, "adding " + child.getAutofillId() + " on session "
+                + session.getContentCaptureSessionId());
+        activity.runOnUiThread(() -> activity.getRootView().addView(child));
+        return child;
+    }
+
+    // TODO(b/123024698): these method are used in cases where we cannot close a session because we
+    // would miss intermediate events, so we need to sleep. This is a hack (it's slow and flaky):
+    // ideally we should block and wait until the service receives the event, but right now
+    // we don't get the service events until after the activity is finished, so we cannot do that...
+    private void waitAndClose(@NonNull ContentCaptureSession session) {
+        Log.d(TAG, "sleeping before closing " + session.getContentCaptureSessionId());
+        sleep();
+        session.close();
+    }
+
+    private void waitAndRemoveViews(@NonNull ChildlessActivity activity, @NonNull View... views) {
+        Log.d(TAG, "sleeping before removing " + Arrays.toString(views));
+        sleep();
+        activity.syncRunOnUiThread(() -> {
+            for (View view : views) {
+                activity.getRootView().removeView(view);
+            }
+        });
+    }
+
+    private void sleep() {
+        Log.d(TAG, "sleeping for 1s ");
+        SystemClock.sleep(1_000);
+    }
+
+    // TODO(b/120494182): temporary hack to get the manager, which currently is only available on
+    // Activity contexts (and would be null from sContext)
+    @NonNull
+    private ContentCaptureManager getContentCaptureManagerHack() throws InterruptedException {
+        final AtomicReference<ContentCaptureManager> ref = new AtomicReference<>();
+        LoginActivity.onRootView(
+                (activity, rootView) -> ref.set(activity.getContentCaptureManager()));
+
+        final ActivityLauncher<LoginActivity> launcher = new ActivityLauncher<>(
+                sContext, mActivitiesWatcher, LoginActivity.class);
+        final ActivityWatcher watcher = launcher.getWatcher();
+        final LoginActivity activity = launcher.launchActivity();
+        watcher.waitFor(RESUMED);
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final ContentCaptureManager mgr = ref.get();
+        assertThat(mgr).isNotNull();
+
+        return mgr;
+    }
+
+    private void setFeatureEnabledByDeviceConfig(@Nullable String value) {
+        Log.d(TAG, "setFeatureEnabledByDeviceConfig(): " + value);
+
+        sKillSwitchManager.set(value);
+    }
+
+    @NonNull
+    private ContentCaptureContext.Builder newContentCaptureContextBuilder(@NonNull String id) {
+        return new ContentCaptureContext.Builder(new LocusId(id));
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
index e01837c..349ff36 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
@@ -25,6 +25,7 @@
 import android.service.contentcapture.ActivityEvent;
 import android.service.contentcapture.ContentCaptureService;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 import android.view.contentcapture.ContentCaptureContext;
@@ -103,7 +104,7 @@
     // but that would make the tests flaker.
 
     /**
-     * Used for testing onDataRemovalRequest.
+     * Used for testing onUserDataRemovalRequest.
      */
     private DataRemovalRequest mRemovalRequest;
 
@@ -281,7 +282,7 @@
 
     @Override
     public void onDataRemovalRequest(DataRemovalRequest request) {
-        Log.i(TAG, "onDataRemovalRequest(id=" + mId + ",req=" + request + ")");
+        Log.i(TAG, "onUserDataRemovalRequest(id=" + mId + ",req=" + request + ")");
         mRemovalRequest = request;
     }
 
@@ -292,7 +293,7 @@
     }
 
     /**
-     * Gets the cached DataRemovalRequest for testing.
+     * Gets the cached UserDataRemovalRequest for testing.
      */
     public DataRemovalRequest getRemovalRequest() {
         return mRemovalRequest;
@@ -503,12 +504,21 @@
         }
 
         /**
-         * Whitelist stuff when the service connects.
+         * Whitelists stuff when the service connects.
          */
         public void whitelist(@Nullable Pair<Set<String>, Set<ComponentName>> whitelist) {
             mWhitelist = whitelist;
         }
 
+       /**
+        * Whitelists just this package.
+        */
+        public void whitelistSelf() {
+            final ArraySet<String> pkgs = new ArraySet<>(1);
+            pkgs.add(MY_PACKAGE);
+            whitelist(new Pair<>(pkgs, null));
+        }
+
         @Override
         public String toString() {
             return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
index 1da0502..10b054b 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
@@ -21,14 +21,14 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
-import android.view.contentcapture.ContentCaptureSession;
 
 import androidx.annotation.NonNull;
 
 import com.android.compatibility.common.util.Visitor;
 
 /**
- * A view that can be used to emulate custom behavior (like virtual children)
+ * A view that can be used to emulate custom behavior (like virtual children) on
+ * {@link #onProvideContentCaptureStructure(ViewStructure, int)}.
  */
 public class CustomView extends View {
 
@@ -38,37 +38,29 @@
 
     public CustomView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES);
     }
 
-    public void onProvideContentCaptureStructure(@NonNull ViewStructure structure) {
-        Log.v(TAG, "onProvideContentCaptureStructure(): delegate=" + mDelegate);
+    @Override
+    public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
         if (mDelegate != null) {
             Log.d(TAG, "onProvideContentCaptureStructure(): delegating");
             structure.setClassName(getAccessibilityClassName().toString());
             mDelegate.visit(structure);
             Log.d(TAG, "onProvideContentCaptureStructure(): delegated");
-        }
-        else {
-            Log.d(TAG, "onProvideContentCaptureStructure(): explicitly setting class name");
-            structure.setClassName(getAccessibilityClassName().toString());
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (changed) {
-            final ContentCaptureSession session = getContentCaptureSession();
-            final ViewStructure structure = session.newViewStructure(this);
-            onProvideContentCaptureStructure(structure);
-            session.notifyViewAppeared(structure);
+        } else {
+            superOnProvideContentCaptureStructure(structure, flags);
         }
     }
 
     @Override
     public CharSequence getAccessibilityClassName() {
-        final String name = CustomView.class.getName();
-        Log.d(TAG, "getAccessibilityClassName(): " + name);
-        return name;
+        return CustomView.class.getName();
+    }
+
+    void superOnProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+        Log.d(TAG, "calling super.onProvideContentCaptureStructure()");
+        super.onProvideContentCaptureStructure(structure, flags);
     }
 
     void setContentCaptureDelegate(@NonNull Visitor<ViewStructure> delegate) {
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
index ad1051c..bae47e5 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
@@ -15,9 +15,13 @@
  */
 package android.contentcaptureservice.cts;
 
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
 import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
 import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
 import static android.contentcaptureservice.cts.Assertions.assertViewWithUnknownParentAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
 
@@ -50,13 +54,13 @@
      * <p>Used on {@link #assertInitialViewsAppeared(Session, int)} and
      * {@link #assertInitialViewsDisappeared(List, int)}.
      */
-    public static final int MIN_EVENTS = 2;
+    public static final int MIN_EVENTS = 7;
 
     CustomView mCustomView;
 
     /**
      * Sets a delegate that provides the behavior of
-     * {@link CustomView#onProvideContentCaptureStructure(ViewStructure)}.
+     * {@link CustomView#onProvideContentCaptureStructure(ViewStructure, int)}.
      */
     static void setCustomViewDelegate(@NonNull DoubleVisitor<CustomView, ViewStructure> delegate) {
         sCustomViewDelegate = delegate;
@@ -126,7 +130,12 @@
 
         // Assert just the relevant events
         assertSessionResumed(events, 0);
-        assertViewWithUnknownParentAppeared(events, 1, session.id, mCustomView);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, getDecorView());
+        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+        assertViewWithUnknownParentAppeared(events, 5, session.id, mCustomView);
+        assertViewTreeFinished(events, 6);
 
         return events;
     }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index f96c3d8..e01ed72 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -15,10 +15,14 @@
  */
 package android.contentcaptureservice.cts;
 
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
 import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
 import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertViewTextChanged;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
 import static android.contentcaptureservice.cts.Assertions.assertViewWithUnknownParentAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertVirtualViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertVirtualViewDisappeared;
@@ -160,15 +164,20 @@
 
         // Assert just the relevant events
         assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, decorView);
+        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
 
         final ContentCaptureSession mainSession = activity.mCustomView.getContentCaptureSession();
-        assertVirtualViewAppeared(events, 1, mainSession, customViewId, 1, "child");
-        assertVirtualViewDisappeared(events, 2, customViewId, mainSession, 1);
+        assertVirtualViewAppeared(events, 5, mainSession, customViewId, 1, "child");
+        assertVirtualViewDisappeared(events, 6, customViewId, mainSession, 1);
 
         // This is the "wrong" part - the parent is notified last
-        assertViewWithUnknownParentAppeared(events, 3, session.id, activity.mCustomView);
+        assertViewWithUnknownParentAppeared(events, 7, session.id, activity.mCustomView);
 
-        assertSessionPaused(events, 4);
+        assertViewTreeFinished(events, 8);
+        assertSessionPaused(events, 9);
 
         activity.assertInitialViewsDisappeared(events, additionalEvents);
     }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index 8e97aac..ed5340c 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -25,6 +25,7 @@
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.Log;
+import android.view.View;
 import android.view.contentcapture.ContentCaptureSession;
 import android.widget.TextView;
 
@@ -118,6 +119,7 @@
     public static TextView newImportantView(@NonNull Context context, @NonNull String text) {
         final TextView child = new TextView(context);
         child.setText(text);
+        child.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES);
         Log.v(TAG, "newImportantView(text=" + text + ", id=" + child.getAutofillId() + ")");
         return child;
     }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
new file mode 100644
index 0000000..3747e2b
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
@@ -0,0 +1,191 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionId;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public class LoginActivity extends AbstractRootViewActivity {
+
+    private static final String TAG = LoginActivity.class.getSimpleName();
+
+    /**
+     * Mininum number of events generated when the activity starts.
+     *
+     * <p>Used on {@link #assertInitialViewsAppeared(Session, int)} and
+     * {@link #assertInitialViewsDisappeared(List, int)}.
+     */
+    public static final int MIN_EVENTS = 11;
+
+    TextView mUsernameLabel;
+    EditText mUsername;
+    TextView mPasswordLabel;
+    EditText mPassword;
+
+    @Override
+    protected void setContentViewOnCreate(Bundle savedInstanceState) {
+        setContentView(R.layout.login_activity);
+
+        mUsernameLabel = findViewById(R.id.username_label);
+        mUsername = findViewById(R.id.username);
+        mPasswordLabel = findViewById(R.id.password_label);
+        mPassword = findViewById(R.id.password);
+    }
+
+    @Override
+    public void assertDefaultEvents(@NonNull Session session) {
+        final int additionalEvents = 0;
+        final List<ContentCaptureEvent> events = assertInitialViewsAppeared(session,
+                additionalEvents);
+        assertInitialViewsDisappeared(events, additionalEvents);
+    }
+
+    /**
+     * Asserts the events generated when this activity was launched, up to the
+     * {@code TYPE_INITIAL_VIEW_HIERARCHY_FINISHED} event.
+     */
+    @NonNull
+    public List<ContentCaptureEvent> assertInitialViewsAppeared(@NonNull Session session,
+            int additionalEvents) {
+        final List<ContentCaptureEvent> events = assertJustInitialViewsAppeared(session,
+                additionalEvents);
+        assertViewTreeFinished(events, MIN_EVENTS - 1);
+
+        return events;
+    }
+
+    /**
+     * Asserts the events generated when this activity was launched, but without the
+     * {@code TYPE_INITIAL_VIEW_HIERARCHY_FINISHED} event.
+     */
+    @NonNull
+    public List<ContentCaptureEvent> assertJustInitialViewsAppeared(@NonNull Session session,
+            int additionalEvents) {
+        final LoginActivity activity = this;
+        final ContentCaptureSessionId sessionId = session.id;
+        assertRightActivity(session, sessionId, activity);
+
+        // Sanity check
+        assertSessionId(sessionId, activity.mUsernameLabel);
+        assertSessionId(sessionId, activity.mUsername);
+        assertSessionId(sessionId, activity.mPassword);
+        assertSessionId(sessionId, activity.mPasswordLabel);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+        // TODO(b/123540067): ideally it should be X so it reflects just the views defined
+        // in the layout - right now it's generating events for 2 intermediate parents
+        // (android:action_mode_bar_stub and android:content), we should try to create an
+        // activity without them
+
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+
+        assertThat(events.size()).isAtLeast(MIN_EVENTS + additionalEvents);
+
+        // TODO(b/123540067): get rid of those intermediated parents
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+        final View decorView = activity.getDecorView();
+        final View rootView = activity.getRootView();
+
+        assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, decorView);
+        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+        assertViewAppeared(events, 5, sessionId, rootView, grandpa1.getAutofillId());
+        assertViewAppeared(events, 6, sessionId, activity.mUsernameLabel, rootId);
+        assertViewAppeared(events, 7, sessionId, activity.mUsername, rootId);
+        assertViewAppeared(events, 8, sessionId, activity.mPasswordLabel, rootId);
+        assertViewAppeared(events, 9, sessionId, activity.mPassword, rootId);
+
+        return events;
+    }
+
+    /**
+     * Asserts the initial views disappeared after the activity was finished.
+     */
+    public void assertInitialViewsDisappeared(@NonNull List<ContentCaptureEvent> events,
+            int additionalEvents) {
+        // TODO(b/122315042): this method is currently a mess, so let's disable for now and properly
+        // fix these assertions later...
+        if (true) return;
+
+        final LoginActivity activity = this;
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+        final View decorView = activity.getDecorView();
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+
+        // Besides the additional events from the test case, we also need to account for the
+        final int i = MIN_EVENTS + additionalEvents;
+
+        assertViewTreeStarted(events, i);
+
+        // TODO(b/122315042): sometimes we get decor view disappareared events, sometimes we don't
+        // As we don't really care about those, let's fix it!
+        try {
+            assertViewsOptionallyDisappeared(events, i + 1,
+                    rootId,
+                    grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+                    activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+                    activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+        } catch (AssertionError e) {
+            Log.e(TAG, "Hack-ignoring assertion without decor view: " + e);
+            // Try again removing it...
+            assertViewsOptionallyDisappeared(events, i + 1,
+                    rootId,
+                    grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+                    decorView.getAutofillId(),
+                    activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+                    activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+        }
+
+        assertViewTreeFinished(events, i + 2);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Log.d(TAG, "AutofillIds: " + "usernameLabel=" + mUsernameLabel.getAutofillId()
+                + ", username=" + mUsername.getAutofillId()
+                + ", passwordLabel=" + mPasswordLabel.getAutofillId()
+                + ", password=" + mPassword.getAutofillId());
+    }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
new file mode 100644
index 0000000..ea22140
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -0,0 +1,783 @@
+/*
+ * 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.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertContextUpdated;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertRightRelationship;
+import static android.contentcaptureservice.cts.Assertions.assertSessionId;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTextChanged;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
+import static android.contentcaptureservice.cts.Helper.newImportantView;
+import static android.view.contentcapture.DataRemovalRequest.FLAG_IS_PREFIX;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+import android.platform.test.annotations.AppModeFull;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataRemovalRequest.LocusIdRequest;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.compatibility.common.util.DoubleVisitor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class LoginActivityTest
+        extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<LoginActivity> {
+
+    private static final String TAG = LoginActivityTest.class.getSimpleName();
+
+    private static final int NO_FLAGS = 0;
+
+    private static final ActivityTestRule<LoginActivity> sActivityRule = new ActivityTestRule<>(
+            LoginActivity.class, false, false);
+
+    public LoginActivityTest() {
+        super(LoginActivity.class);
+    }
+
+    @Override
+    protected ActivityTestRule<LoginActivity> getActivityTestRule() {
+        return sActivityRule;
+    }
+
+    @Before
+    @After
+    public void resetActivityStaticState() {
+        LoginActivity.onRootView(null);
+    }
+
+    @Test
+    public void testSimpleLifecycle_defaultSession() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        activity.assertDefaultEvents(session);
+
+        final ComponentName name = activity.getComponentName();
+        service.assertThat()
+                .activityResumed(name)
+                .activityPaused(name);
+    }
+
+    @Test
+    public void testSimpleLifecycle_rootViewSession() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ContentCaptureContext clientContext = newContentCaptureContext();
+
+        final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>();
+        final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>();
+
+        LoginActivity.onRootView((activity, rootView) -> {
+            final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+            mainSessionRef.set(mainSession);
+            final ContentCaptureSession childSession = mainSession
+                    .createContentCaptureSession(clientContext);
+            childSessionRef.set(childSession);
+            Log.i(TAG, "Setting root view (" + rootView + ") session to " + childSession);
+            rootView.setContentCaptureSession(childSession);
+        });
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final ContentCaptureSessionId mainSessionId = mainSessionRef.get()
+                .getContentCaptureSessionId();
+        final ContentCaptureSessionId childSessionId = childSessionRef.get()
+                .getContentCaptureSessionId();
+        Log.v(TAG, "session ids: main=" + mainSessionId + ", child=" + childSessionId);
+
+        // Sanity checks
+        assertSessionId(childSessionId, activity.getRootView());
+        assertSessionId(childSessionId, activity.mUsernameLabel);
+        assertSessionId(childSessionId, activity.mUsername);
+        assertSessionId(childSessionId, activity.mPassword);
+        assertSessionId(childSessionId, activity.mPasswordLabel);
+
+        // Get the sessions
+        final Session mainSession = service.getFinishedSession(mainSessionId);
+        final Session childSession = service.getFinishedSession(childSessionId);
+
+        assertRightActivity(mainSession, mainSessionId, activity);
+        assertRightRelationship(mainSession, childSession);
+
+        // Sanity check
+        final List<ContentCaptureSessionId> allSessionIds = service.getAllSessionIds();
+        assertThat(allSessionIds).containsExactly(mainSessionId, childSessionId);
+
+        /*
+         * Asserts main session
+         */
+
+        // Checks context
+        assertMainSessionContext(mainSession, activity);
+
+        // Check events
+        final List<ContentCaptureEvent> mainEvents = mainSession.getEvents();
+        Log.v(TAG, "events(" + mainEvents.size() + ") for main session: " + mainEvents);
+
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+        final View decorView = activity.getDecorView();
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+
+        final int minEvents = 7; // TODO(b/122315042): disappeared not always sent
+        assertThat(mainEvents.size()).isAtLeast(minEvents);
+        assertSessionResumed(mainEvents, 0);
+        assertViewTreeStarted(mainEvents, 1);
+        assertDecorViewAppeared(mainEvents, 2, decorView);
+        assertViewAppeared(mainEvents, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(mainEvents, 4, grandpa1, grandpa2.getAutofillId());
+        assertViewTreeFinished(mainEvents, 5);
+        // TODO(b/122315042): these assertions are currently a mess, so let's disable for now and
+        // properly fix them later...
+        if (false) {
+            int pausedIndex = 6;
+            final boolean disappeared = assertViewsOptionallyDisappeared(mainEvents, pausedIndex,
+                    decorView.getAutofillId(),
+                    grandpa2.getAutofillId(), grandpa1.getAutofillId());
+            if (disappeared) {
+                pausedIndex += 3;
+            }
+            assertSessionPaused(mainEvents, pausedIndex);
+        }
+
+        /*
+         * Asserts child session
+         */
+
+        // Checks context
+        assertChildSessionContext(childSession, "file://dev/null");
+
+        assertContentCaptureContext(childSession.context);
+
+        // Check events
+        final List<ContentCaptureEvent> childEvents = childSession.getEvents();
+        Log.v(TAG, "events for child session: " + childEvents);
+        final int minChildEvents = 5;
+        assertThat(childEvents.size()).isAtLeast(minChildEvents);
+        assertViewAppeared(childEvents, 0, childSessionId, activity.getRootView(),
+                grandpa1.getAutofillId());
+        assertViewAppeared(childEvents, 1, childSessionId, activity.mUsernameLabel, rootId);
+        assertViewAppeared(childEvents, 2, childSessionId, activity.mUsername, rootId);
+        assertViewAppeared(childEvents, 3, childSessionId, activity.mPasswordLabel, rootId);
+        assertViewAppeared(childEvents, 4, childSessionId, activity.mPassword, rootId);
+
+        assertViewsOptionallyDisappeared(childEvents, minChildEvents,
+                rootId,
+                activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+                activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+    }
+
+    @Test
+    public void testSimpleLifecycle_changeContextAfterCreate() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        final ContentCaptureContext newContext1 = newContentCaptureContext();
+        final ContentCaptureContext newContext2 = null;
+
+        final View rootView = activity.getRootView();
+        final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+        assertThat(mainSession).isNotNull();
+        Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext1);
+        mainSession.setContentCaptureContext(newContext1);
+        assertContentCaptureContext(mainSession.getContentCaptureContext());
+
+        Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext2);
+        mainSession.setContentCaptureContext(newContext2);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        final int additionalEvents = 2;
+        final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+                additionalEvents);
+
+        final ContentCaptureEvent event1 = assertContextUpdated(events, LoginActivity.MIN_EVENTS);
+        final ContentCaptureContext actualContext = event1.getContentCaptureContext();
+        assertContentCaptureContext(actualContext);
+
+        final ContentCaptureEvent event2 = assertContextUpdated(events,
+                LoginActivity.MIN_EVENTS + 1);
+        assertThat(event2.getContentCaptureContext()).isNull();
+    }
+
+    @Test
+    public void testSimpleLifecycle_changeContextOnCreate() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ContentCaptureContext newContext = newContentCaptureContext();
+
+        LoginActivity.onRootView((activity, rootView) -> {
+            final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+            Log.i(TAG, "Setting root view (" + rootView + ") context to " + newContext);
+            mainSession.setContentCaptureContext(newContext);
+            assertContentCaptureContext(mainSession.getContentCaptureContext());
+        });
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+        final ContentCaptureSessionId sessionId = session.id;
+        assertRightActivity(session, sessionId, activity);
+
+        // Sanity check
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+        // TODO(b/123540067): ideally it should be X so it reflects just the views defined
+        // in the layout - right now it's generating events for 2 intermediate parents
+        // (android:action_mode_bar_stub and android:content), we should try to create an
+        // activity without them
+
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+
+        assertThat(events.size()).isAtLeast(11);
+
+        // TODO(b/123540067): get rid of those intermediated parents
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+        final View decorView = activity.getDecorView();
+        final View rootView = activity.getRootView();
+
+        final ContentCaptureEvent ctxUpdatedEvent = assertContextUpdated(events, 0);
+        final ContentCaptureContext actualContext = ctxUpdatedEvent.getContentCaptureContext();
+        assertContentCaptureContext(actualContext);
+
+        assertSessionResumed(events, 1);
+        assertViewTreeStarted(events, 2);
+        assertDecorViewAppeared(events, 3, decorView);
+        assertViewAppeared(events, 4, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 5, grandpa1, grandpa2.getAutofillId());
+        assertViewAppeared(events, 6, sessionId, rootView, grandpa1.getAutofillId());
+        assertViewAppeared(events, 7, sessionId, activity.mUsernameLabel, rootId);
+        assertViewAppeared(events, 8, sessionId, activity.mUsername, rootId);
+        assertViewAppeared(events, 9, sessionId, activity.mPasswordLabel, rootId);
+        assertViewAppeared(events, 10, sessionId, activity.mPassword, rootId);
+    }
+
+    @Test
+    public void testTextChanged() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername
+                .setText("user"));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.syncRunOnUiThread(() -> {
+            activity.mUsername.setText("USER");
+            activity.mPassword.setText("PASS");
+        });
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        final ContentCaptureSessionId sessionId = session.id;
+
+        assertRightActivity(session, sessionId, activity);
+
+        final int additionalEvents = 2;
+        final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+                additionalEvents);
+
+        final int i = LoginActivity.MIN_EVENTS;
+
+        assertViewTextChanged(events, i, activity.mUsername.getAutofillId(), "USER");
+        assertViewTextChanged(events, i + 1, activity.mPassword.getAutofillId(), "PASS");
+
+        activity.assertInitialViewsDisappeared(events, additionalEvents);
+    }
+
+    @Test
+    public void testTextChangeBuffer() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername
+                .setText(""));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.syncRunOnUiThread(() -> {
+            activity.mUsername.setText("a");
+            activity.mUsername.setText("ab");
+
+            activity.mPassword.setText("d");
+            activity.mPassword.setText("de");
+
+            activity.mUsername.setText("abc");
+        });
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        final ContentCaptureSessionId sessionId = session.id;
+
+        assertRightActivity(session, sessionId, activity);
+
+        final int additionalEvents = 3;
+        final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+                additionalEvents);
+
+        final int i = LoginActivity.MIN_EVENTS;
+
+        assertViewTextChanged(events, i, activity.mUsername.getAutofillId(), "ab");
+        assertViewTextChanged(events, i + 1, activity.mPassword.getAutofillId(), "de");
+        assertViewTextChanged(events, i + 2, activity.mUsername.getAutofillId(), "abc");
+
+        activity.assertInitialViewsDisappeared(events, additionalEvents);
+    }
+
+    @Test
+    public void testDisabledByFlagSecure() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> activity.getWindow()
+                .addFlags(WindowManager.LayoutParams.FLAG_SECURE));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        assertThat((session.context.getFlags()
+                & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        assertThat(events).isEmpty();
+    }
+
+    @Test
+    public void testDisabledByApp() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+                .setContentCaptureEnabled(false));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+
+        activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH"));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        assertThat((session.context.getFlags()
+                & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        assertThat(events).isEmpty();
+    }
+
+    @Test
+    public void testDisabledFlagSecureAndByApp() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> {
+            activity.getContentCaptureManager().setContentCaptureEnabled(false);
+            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+        });
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+        activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH"));
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        assertThat((session.context.getFlags()
+                & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue();
+        assertThat((session.context.getFlags()
+                & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue();
+        final ContentCaptureSessionId sessionId = session.id;
+        Log.v(TAG, "session id: " + sessionId);
+
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        assertThat(events).isEmpty();
+    }
+
+    @Test
+    public void testUserDataRemovalRequest_forEverything() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+                .removeData(new DataRemovalRequest.Builder().forEverything()
+                        .build()));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        DataRemovalRequest request = service.getRemovalRequest();
+        assertThat(request).isNotNull();
+        assertThat(request.isForEverything()).isTrue();
+        assertThat(request.getLocusIdRequests()).isNull();
+        assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+    }
+
+    @Test
+    public void testUserDataRemovalRequest_oneId() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final LocusId locusId = new LocusId("com.example");
+
+        LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+                .removeData(new DataRemovalRequest.Builder()
+                        .addLocusId(locusId, NO_FLAGS)
+                        .build()));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        DataRemovalRequest request = service.getRemovalRequest();
+        assertThat(request).isNotNull();
+        assertThat(request.isForEverything()).isFalse();
+        assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+
+        final List<LocusIdRequest> requests = request.getLocusIdRequests();
+        assertThat(requests.size()).isEqualTo(1);
+
+        final LocusIdRequest actualRequest = requests.get(0);
+        assertThat(actualRequest.getLocusId()).isEqualTo(locusId);
+        assertThat(actualRequest.getFlags()).isEqualTo(NO_FLAGS);
+    }
+
+    @Test
+    public void testUserDataRemovalRequest_manyIds() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final LocusId locusId1 = new LocusId("com.example");
+        final LocusId locusId2 = new LocusId("com.example2");
+
+        LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+                .removeData(new DataRemovalRequest.Builder()
+                        .addLocusId(locusId1, NO_FLAGS)
+                        .addLocusId(locusId2, FLAG_IS_PREFIX)
+                        .build()));
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final DataRemovalRequest request = service.getRemovalRequest();
+        assertThat(request).isNotNull();
+        assertThat(request.isForEverything()).isFalse();
+        assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+
+        final List<LocusIdRequest> requests = request.getLocusIdRequests();
+        assertThat(requests.size()).isEqualTo(2);
+
+        final LocusIdRequest actualRequest1 = requests.get(0);
+        assertThat(actualRequest1.getLocusId()).isEqualTo(locusId1);
+        assertThat(actualRequest1.getFlags()).isEqualTo(NO_FLAGS);
+
+        final LocusIdRequest actualRequest2 = requests.get(1);
+        assertThat(actualRequest2.getLocusId()).isEqualTo(locusId2);
+        assertThat(actualRequest2.getFlags()).isEqualTo(FLAG_IS_PREFIX);
+    }
+
+    @Test
+    public void testAddChildren_rightAway() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+        final View[] children = new View[2];
+
+        final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity,
+                rootView) -> {
+            final TextView child1 = newImportantView(activity, "c1");
+            children[0] = child1;
+            Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1);
+            rootView.addView(child1);
+            final TextView child2 = newImportantView(activity, "c1");
+            children[1] = child2;
+            Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2);
+            rootView.addView(child2);
+        };
+        LoginActivity.onRootView(visitor);
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        final ContentCaptureSessionId sessionId = session.id;
+        assertRightActivity(session, sessionId, activity);
+
+        final List<ContentCaptureEvent> events = activity.assertJustInitialViewsAppeared(session,
+                /* additionalEvents= */ 2);
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+        int i = LoginActivity.MIN_EVENTS - 1;
+        assertViewAppeared(events, i, sessionId, children[0], rootId);
+        assertViewAppeared(events, i + 1, sessionId, children[1], rootId);
+        assertViewTreeFinished(events, i + 2);
+
+        activity.assertInitialViewsDisappeared(events, children.length);
+    }
+
+    @Test
+    public void testAddChildren_afterAnimation() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+        final View[] children = new View[2];
+
+        final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity,
+                rootView) -> {
+            final TextView child1 = newImportantView(activity, "c1");
+            children[0] = child1;
+            Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1);
+            rootView.addView(child1);
+            final TextView child2 = newImportantView(activity, "c1");
+            children[1] = child2;
+            Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2);
+            rootView.addView(child2);
+        };
+        LoginActivity.onAnimationComplete(visitor);
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        final ContentCaptureSessionId sessionId = session.id;
+        assertRightActivity(session, sessionId, activity);
+        final int additionalEvents = 2; // 2 children views
+        final List<ContentCaptureEvent> events = activity.assertJustInitialViewsAppeared(session,
+                additionalEvents);
+        assertThat(events.size()).isAtLeast(LoginActivity.MIN_EVENTS + 5);
+        final View decorView = activity.getDecorView();
+        final View grandpa1 = activity.getGrandParent();
+        final View grandpa2 = activity.getGrandGrandParent();
+        final AutofillId rootId = activity.getRootView().getAutofillId();
+        int i = LoginActivity.MIN_EVENTS - 1;
+
+        assertViewTreeFinished(events, i);
+        assertViewTreeStarted(events, i + 1);
+        assertViewAppeared(events, i + 2, sessionId, children[0], rootId);
+        assertViewAppeared(events, i + 3, sessionId, children[1], rootId);
+        assertViewTreeFinished(events, i + 4);
+
+        // TODO(b/122315042): assert parents disappeared
+        if (true) return;
+
+        // TODO(b/122315042): sometimes we get decor view disappareared events, sometimes we don't
+        // As we don't really care about those, let's fix it!
+        try {
+            assertViewsOptionallyDisappeared(events, LoginActivity.MIN_EVENTS + additionalEvents,
+                    rootId,
+                    grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+                    activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+                    activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId(),
+                    children[0].getAutofillId(), children[1].getAutofillId());
+        } catch (AssertionError e) {
+            Log.e(TAG, "Hack-ignoring assertion without decor view: " + e);
+            // Try again removing it...
+            assertViewsOptionallyDisappeared(events, LoginActivity.MIN_EVENTS + additionalEvents,
+                    rootId,
+                    grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+                    decorView.getAutofillId(),
+                    activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+                    activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId(),
+                    children[0].getAutofillId(), children[1].getAutofillId());
+
+        }
+    }
+
+    @Test
+    public void testWhitelist_packageNotWhitelisted() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        service.setContentCaptureWhitelist((Set) null, (Set) null);
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        assertThat(service.getAllSessionIds()).isEmpty();
+    }
+
+    @Test
+    public void testWhitelist_activityNotWhitelisted() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ArraySet<ComponentName> components = new ArraySet<>();
+        components.add(new ComponentName(MY_PACKAGE, "some.activity"));
+        service.setContentCaptureWhitelist(null, components);
+        final ActivityWatcher watcher = startWatcher();
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        assertThat(service.getAllSessionIds()).isEmpty();
+    }
+
+    /**
+     * Creates a context that can be assert by
+     * {@link #assertContentCaptureContext(ContentCaptureContext)}.
+     */
+    private ContentCaptureContext newContentCaptureContext() {
+        final String id = "file://dev/null";
+        final Bundle bundle = new Bundle();
+        bundle.putString("DUDE", "SWEET");
+        return new ContentCaptureContext.Builder(new LocusId(id)).setExtras(bundle).build();
+    }
+
+    /**
+     * Asserts a context that can has been created by {@link #newContentCaptureContext()}.
+     */
+    private void assertContentCaptureContext(@NonNull ContentCaptureContext context) {
+        assertWithMessage("null context").that(context).isNotNull();
+        assertWithMessage("wrong ID on context %s", context).that(context.getLocusId().getId())
+                .isEqualTo("file://dev/null");
+        final Bundle extras = context.getExtras();
+        assertWithMessage("no extras on context %s", context).that(extras).isNotNull();
+        assertWithMessage("wrong number of extras on context %s", context).that(extras.size())
+                .isEqualTo(1);
+        assertWithMessage("wrong extras on context %s", context).that(extras.getString("DUDE"))
+                .isEqualTo("SWEET");
+    }
+
+    // TODO(b/123540602): add moar test cases for different sessions:
+    // - session1 on rootView, session2 on children
+    // - session1 on rootView, session2 on child1, session3 on child2
+    // - combination above where the CTS test explicitly finishes a session
+
+    // TODO(b/123540602): add moar test cases for different scenarios, like:
+    // - dynamically adding /
+    // - removing views
+    // - pausing / resuming activity / tapping home
+    // - changing text
+    // - secure flag with child sessions
+    // - making sure events are flushed when activity pause / resume
+
+    // TODO(b/126262658): moar lifecycle events, like multiple activities.
+
+}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 4f8fe3d..a674c86 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -272,6 +272,7 @@
         </service>
 
         <activity android:name="android.server.wm.AlertWindowsAppOpsTestsActivity"/>
+        <activity android:name="android.server.wm.CloseOnOutsideTestActivity" />
         <activity android:name="android.server.wm.DialogFrameTestActivity" />
         <activity android:name="android.server.wm.DisplayCutoutTests$TestActivity"
                   android:turnScreenOn="true"
diff --git a/tests/framework/base/windowmanager/app27/AndroidManifest.xml b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
index ab9dbc0..66da0e6 100755
--- a/tests/framework/base/windowmanager/app27/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
@@ -34,6 +34,17 @@
                   android:exported="true"
         />
 
+        <activity android:name=".LaunchEnterPipActivity"
+                  android:exported="true"
+        />
+
+        <activity android:name=".PipActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+        />
+
         <activity android:name=".HomeActivity"
                         android:enabled="false"
                         android:exported="true">
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
index fd05e74..bf59582 100644
--- a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
@@ -33,6 +33,12 @@
     public static final ComponentName SDK_27_SEPARATE_PROCESS_ACTIVITY =
             component(Components.class, "SeparateProcessActivity");
 
+    public static final ComponentName SDK_27_LAUNCH_ENTER_PIP_ACTIVITY =
+            component(Components.class, "LaunchEnterPipActivity");
+
+    public static final ComponentName SDK_27_PIP_ACTIVITY =
+            component(Components.class, "PipActivity");
+
     public static final ComponentName SDK_27_HOME_ACTIVITY =
             component(Components.class, "HomeActivity");
 }
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java
new file mode 100644
index 0000000..ad1e431
--- /dev/null
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm.app27;
+
+import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchEnterPipActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+            startActivity(new Intent(this, PipActivity.class));
+        }
+    }
+}
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java
new file mode 100644
index 0000000..6bcf8fa
--- /dev/null
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm.app27;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.os.Bundle;
+import android.util.Rational;
+
+public class PipActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        enterPictureInPictureMode(
+                new PictureInPictureParams.Builder()
+                        .setAspectRatio(new Rational(1, 1))
+                        .build());
+    }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml b/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
index 9e98b64..eb156b6 100755
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/AndroidManifest.xml
@@ -18,6 +18,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.server.wm.backgroundactivity.appa">
 
+    <!-- To enable the app to start activities from the background. -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
     <application android:testOnly="true">
         <receiver
             android:name=".StartBackgroundActivityReceiver"
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index 0a51a0d..b1a559f 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -16,6 +16,8 @@
 
 package android.server.wm;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.server.wm.ActivityManagerState.STATE_INITIALIZING;
 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
 import static android.server.wm.ComponentNameUtils.getActivityName;
@@ -58,9 +60,10 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 
+import com.android.compatibility.common.util.AppOpsUtils;
+
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.List;
@@ -77,6 +80,7 @@
 public class BackgroundActivityLaunchTest extends ActivityManagerTestBase {
 
     private static final int ACTIVITY_FOCUS_TIMEOUT_MS = 3000;
+    private static final String APP_A_PACKAGE_NAME = APP_A_FOREGROUND_ACTIVITY.getPackageName();
 
     private static final String TEST_PACKAGE_APP_A = "android.server.wm.backgroundactivity.appa";
     private static final String TEST_PACKAGE_APP_B = "android.server.wm.backgroundactivity.appb";
@@ -87,6 +91,11 @@
         mAm = mContext.getSystemService(ActivityManager.class);
         mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
+        // disable SAW appopp for AppA (it's granted autonatically when installed in CTS)
+        AppOpsUtils.setOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window", MODE_ERRORED);
+        assertEquals(AppOpsUtils.getOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window"),
+                MODE_ERRORED);
+
         pressWakeupButton();
         pressUnlockButton();
         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
@@ -105,6 +114,7 @@
         stopTestPackage(TEST_PACKAGE_APP_A);
         stopTestPackage(TEST_PACKAGE_APP_B);
         pressHomeButton();
+        AppOpsUtils.reset(APP_A_PACKAGE_NAME);
         mAmWmState.waitForHomeActivityVisible();
         runWithShellPermissionIdentity(() -> {
             runShellCommand("dpm remove-active-admin --user current "
@@ -137,6 +147,21 @@
     }
 
     @Test
+    public void testBackgroundActivityNotBlockedWhenSystemAlertWindowGranted() throws Exception {
+        // enable appopp for SAW for this test
+        AppOpsUtils.setOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window", MODE_ALLOWED);
+        assertEquals(AppOpsUtils.getOpMode(APP_A_PACKAGE_NAME, "android:system_alert_window"),
+                MODE_ALLOWED);
+
+        // Start AppA background activity successfully as the package has SAW
+        Intent intent = new Intent();
+        intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
+        mContext.sendBroadcast(intent);
+        boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
+        assertTrue("Not able to start foreground activity", result);
+    }
+
+    @Test
     public void testBackgroundActivityNotBlockedWhenForegroundActivityExists() throws Exception {
         // Start AppA foreground activity
         Intent intent = new Intent();
@@ -172,7 +197,6 @@
                 APP_A_FOREGROUND_ACTIVITY);
     }
 
-    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     public void testActivityNotBlockedwhenForegroundActivityLaunchInSameTask() throws Exception {
         // Start foreground activity, and foreground activity able to launch background activity
@@ -203,7 +227,6 @@
                 APP_A_FOREGROUND_ACTIVITY);
     }
 
-    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     public void testActivityNotBlockedWhenForegroundActivityLaunchInDifferentTask()
             throws Exception {
@@ -236,7 +259,6 @@
         assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
     }
 
-    @Ignore // test temporarily disabled due to bg activity start grace period introduction
     @Test
     @FlakyTest(bugId = 130800326)
     public void testActivityBlockedWhenForegroundActivityRestartsItself() throws Exception {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
index c83cb2e..a1c522c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
@@ -29,9 +29,7 @@
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
 
 import android.content.ComponentName;
 import android.platform.test.annotations.Presubmit;
@@ -40,9 +38,6 @@
 
 import org.junit.Test;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:AmStartOptionsTests
@@ -92,9 +87,10 @@
         // Start LaunchingActivity again and finish TestActivity
         final int flags =
                 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP;
-        final String result = executeShellCommand(
-                "am start -W -f " + flags + " -n " + getActivityName(LAUNCHING_ACTIVITY));
-        verifyShellOutput(result, LAUNCHING_ACTIVITY, false);
+        executeShellCommand("am start -W -f " + flags + " -n " + getActivityName(LAUNCHING_ACTIVITY)
+                + " --display " + DEFAULT_DISPLAY);
+        waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
+                "Activity must be launched.");
     }
 
     private void testDashW(final ComponentName entryActivity, final ComponentName actualActivity)
@@ -112,73 +108,14 @@
 
     private void startActivityAndVerifyResult(final ComponentName entryActivity,
             final ComponentName actualActivity, boolean shouldStart) {
-        // See TODO below
-        // final LogSeparator logSeparator = separateLogs();
-
         // Pass in different data only when cold starting. This is to make the intent
         // different in subsequent warm/hot launches, so that the entrypoint alias
         // activity is always started, but the actual activity is not started again
         // because of the NEW_TASK and singleTask flags.
-        final String result = executeShellCommand(
-                "am start -n " + getActivityName(entryActivity) + " -W"
-                + (shouldStart ? " -d about:blank" : ""));
+        executeShellCommand("am start -n " + getActivityName(entryActivity) + " -W --display "
+                + DEFAULT_DISPLAY + (shouldStart ? " -d about:blank" : ""));
 
-        // Verify shell command return value
-        verifyShellOutput(result, actualActivity, shouldStart);
-
-        // TODO: Disable logcat check for now.
-        // Logcat of WM or AM tag could be lost (eg. chatty if earlier events generated
-        // too many lines), and make the test look flaky. We need to either use event
-        // log or swith to other mechanisms. Only verify shell output for now, it should
-        // still catch most failures.
-
-        // Verify adb logcat log
-        //verifyLogcat(actualActivity, shouldStart, logSeparator);
+        waitAndAssertTopResumedActivity(actualActivity, DEFAULT_DISPLAY,
+                "Activity must be launched");
     }
-
-    private static final Pattern sNotStartedWarningPattern = Pattern.compile(
-            "Warning: Activity not started(.*)");
-    private static final Pattern sStatusPattern = Pattern.compile(
-            "Status: (.*)");
-    private static final Pattern sActivityPattern = Pattern.compile(
-            "Activity: (.*)");
-    private static final String sStatusOk = "ok";
-
-    private void verifyShellOutput(
-            final String result, final ComponentName activity, boolean shouldStart) {
-        boolean warningFound = false;
-        String status = null;
-        String reportedActivity = null;
-
-        final String[] lines = result.split("\\n");
-        // Going from the end of logs to beginning in case if some other activity is started first.
-        for (int i = lines.length - 1; i >= 0; i--) {
-            final String line = lines[i].trim();
-            Matcher matcher = sNotStartedWarningPattern.matcher(line);
-            if (matcher.matches()) {
-                warningFound = true;
-                continue;
-            }
-            matcher = sStatusPattern.matcher(line);
-            if (matcher.matches()) {
-                status = matcher.group(1);
-                continue;
-            }
-            matcher = sActivityPattern.matcher(line);
-            if (matcher.matches()) {
-                reportedActivity = matcher.group(1);
-                continue;
-            }
-        }
-
-        assertEquals("Status is ok", sStatusOk, status);
-        assertEquals("Reported activity is " +  getActivityName(activity),
-                getActivityName(activity), reportedActivity);
-
-        if (shouldStart && warningFound) {
-            fail("Should start new activity but brought something to front.");
-        } else if (!shouldStart && !warningFound){
-            fail("Should bring existing activity to front but started new activity.");
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java
new file mode 100644
index 0000000..5f4a88d
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+
+/**
+ * Activity that makes its Window half width/height so that an area exists outside which can be
+ * tapped to close it when {@link Activity#setFinishOnTouchOutside(boolean)} is enabled.
+ */
+public class CloseOnOutsideTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        int width = displayMetrics.widthPixels;
+        int height = displayMetrics.heightPixels;
+
+        LayoutParams params = getWindow().getAttributes();
+        params.width = width / 2;
+        params.height = height / 2;
+        getWindow().setAttributes(params);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java
new file mode 100644
index 0000000..313c5f5
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Instrumentation;
+import android.util.DisplayMetrics;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ShellUtils;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link android.view.Window#setCloseOnTouchOutside(boolean)} through exposed Activity API.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CloseOnOutsideTests {
+
+    @Rule
+    public final ActivityTestRule<CloseOnOutsideTestActivity> mTestActivity =
+            new ActivityTestRule<>(CloseOnOutsideTestActivity.class, true, true);
+
+    @Test
+    public void withDefaults() {
+        touchAndAssert(false /* shouldBeFinishing */);
+    }
+
+    @Test
+    public void finishTrue() {
+        mTestActivity.getActivity().setFinishOnTouchOutside(true);
+        touchAndAssert(true /* shouldBeFinishing */);
+    }
+
+    @Test
+    public void finishFalse() {
+        mTestActivity.getActivity().setFinishOnTouchOutside(false);
+        touchAndAssert(false /* shouldBeFinishing */);
+    }
+
+    // Tap the bottom right and check the Activity is finishing
+    private void touchAndAssert(boolean shouldBeFinishing) {
+        DisplayMetrics displayMetrics =
+                mTestActivity.getActivity().getResources().getDisplayMetrics();
+        int width = (int) (displayMetrics.widthPixels * 0.875f);
+        int height = (int) (displayMetrics.heightPixels * 0.875f);
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        // To be safe, make sure nothing else is finishing the Activity
+        instrumentation.runOnMainSync(() -> assertFalse(mTestActivity.getActivity().isFinishing()));
+
+        ShellUtils.runShellCommand("input tap %d %d", width, height);
+
+        instrumentation.runOnMainSync(
+                () -> assertEquals(shouldBeFinishing, mTestActivity.getActivity().isFinishing()));
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/EnsureBarContrastTest.java b/tests/framework/base/windowmanager/src/android/server/wm/EnsureBarContrastTest.java
index 331716a..4830da0 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/EnsureBarContrastTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/EnsureBarContrastTest.java
@@ -19,6 +19,10 @@
 import static android.server.wm.EnsureBarContrastTest.TestActivity.EXTRA_ENSURE_CONTRAST;
 import static android.server.wm.EnsureBarContrastTest.TestActivity.EXTRA_LIGHT_BARS;
 import static android.server.wm.EnsureBarContrastTest.TestActivity.backgroundForBar;
+import static android.server.wm.BarTestUtils.assumeHasColoredBars;
+import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar;
+import static android.server.wm.BarTestUtils.assumeHasColoredStatusBar;
+import static android.server.wm.BarTestUtils.isAssumptionViolated;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -37,7 +41,6 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.PollingCheck;
@@ -45,6 +48,7 @@
 import org.hamcrest.CustomTypeSafeMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
+import org.junit.AssumptionViolatedException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
@@ -84,8 +88,13 @@
     }
 
     public void runTestEnsureContrast(boolean lightBars) {
+        assumeHasColoredBars();
         TestActivity activity = launchAndWait(mTestActivity, lightBars, true /* ensureContrast */);
         for (Bar bar : Bar.BARS) {
+            if (isAssumptionViolated(() -> bar.checkAssumptions(mTestActivity))) {
+                continue;
+            }
+
             Bitmap bitmap = getOnMainSync(() -> activity.screenshotBar(bar, mDumper));
 
             if (getOnMainSync(() -> activity.barIsTapThrough(bar))) {
@@ -307,6 +316,11 @@
                 r.bottom = r.top + getInset(insets);
                 return r;
             }
+
+            @Override
+            void checkAssumptions(ActivityTestRule<?> rule) throws AssumptionViolatedException {
+                assumeHasColoredStatusBar(rule);
+            }
         };
 
         static final Bar NAVIGATION = new Bar("Navigation") {
@@ -321,6 +335,11 @@
                 r.top = r.bottom - getInset(insets);
                 return r;
             }
+
+            @Override
+            void checkAssumptions(ActivityTestRule<?> rule) throws AssumptionViolatedException {
+                assumeHasColoredNavigationBar(rule);
+            }
         };
 
         static final Bar[] BARS = {STATUS, NAVIGATION};
@@ -334,5 +353,7 @@
         abstract int getInset(Insets insets);
 
         abstract Rect getLocation(Insets insets, Rect screen);
+
+        abstract void checkAssumptions(ActivityTestRule<?> rule) throws AssumptionViolatedException;
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index e01c51c..98669b7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -60,6 +60,8 @@
 import static android.server.wm.app.Components.TestActivity.EXTRA_CONFIGURATION;
 import static android.server.wm.app.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
 import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
+import static android.server.wm.app27.Components.SDK_27_LAUNCH_ENTER_PIP_ACTIVITY;
+import static android.server.wm.app27.Components.SDK_27_PIP_ACTIVITY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -752,6 +754,30 @@
     }
 
     @Test
+    public void testLaunchStoppedActivityWithPiPInSameProcessPreQ() {
+        // Try to enter picture-in-picture from an activity that has more than one activity in the
+        // task and ensure that it works, for pre-Q app
+        launchActivity(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY,
+                EXTRA_ENTER_PIP, "true");
+        waitForEnterPip(SDK_27_PIP_ACTIVITY);
+        assertPinnedStackExists();
+
+        // Puts the host activity to stopped state
+        launchHomeActivity();
+        mAmWmState.assertHomeActivityVisible(true);
+        waitAndAssertActivityState(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, STATE_STOPPED,
+                "Activity should become STOPPED");
+        mAmWmState.assertVisibility(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, false);
+
+        // Host activity should be visible after re-launch and PiP window still exists
+        launchActivity(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY);
+        waitAndAssertActivityState(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, STATE_RESUMED,
+                "Activity should become RESUMED");
+        mAmWmState.assertVisibility(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, true);
+        assertPinnedStackExists();
+    }
+
+    @Test
     public void testEnterPipWithResumeWhilePausingActivityNoStop() throws Exception {
         /*
          * Launch the resumeWhilePausing activity and ensure that the PiP activity did not get
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
index 9a6d134..a361011 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -231,6 +231,7 @@
      * - The window which lost top-focus can be notified about pointer-capture lost.
      */
     @Test
+    @FlakyTest(bugId = 135574991)
     public void testPointerCapture() throws InterruptedException {
         final PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class,
                 DEFAULT_DISPLAY);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
index f73d5a6..2ec92bb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
@@ -168,7 +168,7 @@
                 insets.getMandatorySystemGestureInsets(),
                 insetsLessThanOrEqualTo(insets.getSystemGestureInsets()));
 
-        Insets stableAndSystem = Insets.max(insets.getSystemGestureInsets(),
+        Insets stableAndSystem = Insets.max(insets.getSystemWindowInsets(),
                 insets.getStableInsets());
         assertThat("mandatory system gesture insets must include intersection between "
                         + "stable and system window insets",
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 8601d35..2f88d31 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -929,7 +929,10 @@
                 "doze_always_on",
                 "doze_pulse_on_pick_up",
                 "doze_pulse_on_long_press",
-                "doze_pulse_on_double_tap"
+                "doze_pulse_on_double_tap",
+                "doze_wake_screen_gesture",
+                "doze_wake_display_gesture",
+                "doze_tap_gesture"
         };
 
         private String get(String key) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java b/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java
new file mode 100644
index 0000000..2d524df
--- /dev/null
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.util.Log;
+import android.view.WindowInsets;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.AssumptionViolatedException;
+
+/**
+ * Common assumptions for system bar tests.
+ *
+ * TODO: Unify with copy in systemui tests.
+ */
+public final class BarTestUtils {
+
+    private BarTestUtils() {
+    }
+
+    public static void assumeHasColoredStatusBar(ActivityTestRule<?> rule) {
+        assumeHasColoredBars();
+
+        assumeFalse("No status bar when running in VR", isRunningInVr());
+
+        assumeTrue("Top stable inset is non-positive, no status bar.",
+                getInsets(rule).getStableInsetTop() > 0);
+    }
+
+    public static void assumeHasColoredNavigationBar(ActivityTestRule<?> rule) {
+        assumeHasColoredBars();
+
+        assumeTrue("Bottom stable inset is non-positive, no navigation bar",
+                getInsets(rule).getStableInsetBottom() > 0);
+    }
+
+    public static void assumeHasColoredBars() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+
+        assumeFalse("Embedded devices don't have system bars",
+                getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_EMBEDDED));
+
+        assumeFalse("No bars on watches and TVs", pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+
+        assumeFalse("Automotive navigation bar is opaque",
+                pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+
+        assumeTrue("Only highEndGfx devices have colored system bars",
+                ActivityManager.isHighEndGfx());
+    }
+
+    private static boolean isRunningInVr() {
+        final Context context = InstrumentationRegistry.getContext();
+        final Configuration config = context.getResources().getConfiguration();
+        return (config.uiMode & Configuration.UI_MODE_TYPE_MASK)
+                == Configuration.UI_MODE_TYPE_VR_HEADSET;
+    }
+
+    private static WindowInsets getInsets(ActivityTestRule<?> rule) {
+        final WindowInsets[] insets = new WindowInsets[1];
+        try {
+            rule.runOnUiThread(() -> {
+                insets[0] = rule.getActivity().getWindow().getDecorView().getRootWindowInsets();
+            });
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+        return insets[0];
+    }
+
+    public static boolean isAssumptionViolated(Runnable assumption) {
+        try {
+            assumption.run();
+            return true;
+        } catch (AssumptionViolatedException e) {
+            Log.i("BarTestUtils", "Assumption violated", e);
+            return false;
+        }
+    }
+}
diff --git a/tests/openglperf2/AndroidTest.xml b/tests/openglperf2/AndroidTest.xml
index 22ceed7..c2fa3a6 100644
--- a/tests/openglperf2/AndroidTest.xml
+++ b/tests/openglperf2/AndroidTest.xml
@@ -26,5 +26,7 @@
     <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/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
index 5ac0141..e4abb12 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
@@ -68,6 +68,7 @@
     private static final String TAG = "BatterySaverAlarmTest";
 
     private static final long DEFAULT_WAIT = 1_000;
+    private static final long THROTTLED_WAIT = 5_000;
 
     // Tweaked alarm manager constants to facilitate testing
     private static final long MIN_REPEATING_INTERVAL = 5_000;
@@ -186,7 +187,8 @@
         forcePackageIntoBg(targetPackage);
 
         // First alarm shouldn't be throttled.
-        final long triggerElapsed1 = SystemClock.elapsedRealtime() + MIN_FUTURITY;
+        long now = SystemClock.elapsedRealtime();
+        final long triggerElapsed1 = now + MIN_FUTURITY;
         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed1);
         ThreadUtils.sleepUntilRealtime(triggerElapsed1 + DEFAULT_WAIT);
         assertEquals("Allow-while-idle alarm shouldn't be blocked in battery saver",
@@ -195,14 +197,30 @@
         // Second one should be throttled.
         mAlarmCount.set(0);
 
-        final long triggerElapsed2 = triggerElapsed1 + ALLOW_WHILE_IDLE_SHORT_TIME;
+        // Check that the alarm scheduled at triggerElapsed2
+        // fires between triggerElapsed2 and (triggerElapsed3+THROTTLED_WAIT).
+        now = SystemClock.elapsedRealtime();
+        final long triggerElapsed2 = now + ALLOW_WHILE_IDLE_SHORT_TIME;
+        final long triggerElapsed3 = now + ALLOW_WHILE_IDLE_LONG_TIME;
         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed2);
-        ThreadUtils.sleepUntilRealtime(triggerElapsed2 + DEFAULT_WAIT);
-        assertEquals("Follow up allow-while-idle alarm shouldn't go off before short time",
-                0, mAlarmCount.get());
 
-        final long triggerElapsed3 = triggerElapsed1 + ALLOW_WHILE_IDLE_LONG_TIME;
-        ThreadUtils.sleepUntilRealtime(triggerElapsed3 + DEFAULT_WAIT);
+        // Check the time first before checking the alarm counter to avoid a
+        // situation when the alarm fires between sleepUntilRealtime and
+        // assertEquals.
+        while (true) {
+            Thread.sleep(DEFAULT_WAIT);
+
+            final int alarmCount = mAlarmCount.get();
+            if (SystemClock.elapsedRealtime() < triggerElapsed2) {
+                assertEquals("Follow up allow-while-idle alarm shouldn't go off "
+                        + "before short time",
+                        0, alarmCount);
+            } else {
+                break;
+            }
+        }
+
+        ThreadUtils.sleepUntilRealtime(triggerElapsed3 + THROTTLED_WAIT);
         assertEquals("Follow-up allow-while-idle alarm should go off after long time",
                 1, mAlarmCount.get());
 
@@ -211,7 +229,8 @@
 
         startService(targetPackage, true);
 
-        final long triggerElapsed4 = triggerElapsed3 + ALLOW_WHILE_IDLE_SHORT_TIME;
+        now = SystemClock.elapsedRealtime();
+        final long triggerElapsed4 = now + ALLOW_WHILE_IDLE_SHORT_TIME;
         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed4);
         ThreadUtils.sleepUntilRealtime(triggerElapsed4 + DEFAULT_WAIT);
         assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
@@ -224,7 +243,8 @@
 
         mAlarmCount.set(0);
 
-        final long triggerElapsed5 = triggerElapsed4 + ALLOW_WHILE_IDLE_SHORT_TIME;
+        now = SystemClock.elapsedRealtime();
+        final long triggerElapsed5 = now + ALLOW_WHILE_IDLE_SHORT_TIME;
         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed5);
         ThreadUtils.sleepUntilRealtime(triggerElapsed5 + DEFAULT_WAIT);
         assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
@@ -234,7 +254,8 @@
         // One more time.
         mAlarmCount.set(0);
 
-        final long triggerElapsed6 = triggerElapsed5 + ALLOW_WHILE_IDLE_SHORT_TIME;
+        now = SystemClock.elapsedRealtime();
+        final long triggerElapsed6 = now + ALLOW_WHILE_IDLE_SHORT_TIME;
         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed6);
         ThreadUtils.sleepUntilRealtime(triggerElapsed6 + DEFAULT_WAIT);
         assertEquals("Allow-while-idle alarm shouldn't be throttled when BS is off",
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
index 8bc9783..ab44335 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
@@ -82,7 +82,7 @@
 
             // Unplug the charger.
             runDumpsysBatteryUnplug();
-
+            Thread.sleep(1000);
             // Verify battery saver gets toggled.
             manager.setPowerSaveModeEnabled(true);
             assertTrue(manager.isPowerSaveMode());
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
index aaf5de0..e2cf479 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -15,6 +15,7 @@
  */
 package android.os.cts.batterysaving;
 
+import static com.android.compatibility.common.util.BatteryUtils.enableBatterySaver;
 import static com.android.compatibility.common.util.BatteryUtils.runDumpsysBatteryReset;
 import static com.android.compatibility.common.util.BatteryUtils.turnOnScreen;
 import static com.android.compatibility.common.util.SystemUtil.runCommandAndPrintOnLogcat;
@@ -70,6 +71,7 @@
         protected void onAfter(Statement base, Description description) throws Throwable {
             runDumpsysBatteryReset();
             turnOnScreen(true);
+            enableBatterySaver(false);
         }
     };
 
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index ca6ff3e..69b364e9 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -24,6 +24,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentProviderClient;
@@ -94,7 +95,6 @@
     // 11.1.17.1
     private static final int MAX_LOGICAL_CHANNEL = 3;
     // Class bytes. The logical channel used should be included for bits b2b1. TS 102 221 Table 11.5
-    private static final String CLA_ENVELOPE = "80";
     private static final int CLA_GET_RESPONSE = 0x00;
     private static final int CLA_MANAGE_CHANNEL = 0x00;
     private static final int CLA_READ_BINARY = 0x00;
@@ -102,7 +102,6 @@
     private static final int CLA_STATUS = 0x80;
     private static final String CLA_STATUS_STRING = "80";
     // APDU Instruction Bytes. TS 102 221 Section 10.1.2
-    private static final String COMMAND_ENVELOPE = "C2";
     private static final int COMMAND_GET_RESPONSE = 0xC0;
     private static final int COMMAND_MANAGE_CHANNEL = 0x70;
     private static final int COMMAND_READ_BINARY = 0xB0;
@@ -903,6 +902,8 @@
      * This test verifies that {@link TelephonyManager#setVoiceMailNumber(String, String)} correctly
      * sets the VoiceMail alpha tag and number when called.
      */
+    /* Disabling the test for now due to a bug in the code. Will re-enable it when the bug is
+       fixed.
     public void testVoiceMailNumber() {
         if (!hasCellular) return;
 
@@ -922,7 +923,7 @@
             // Reset original alpha tag and number values.
             mTelephonyManager.setVoiceMailNumber(originalAlphaTag, originalNumber);
         }
-    }
+    } */
 
     /**
      * This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly
@@ -1095,16 +1096,10 @@
                 + COMMAND_STATUS_STRING
                 + "00" // p1: no indication of application status
                 + "00"; // p2: identical parameters to
-        String lc = "0" + (envelope.length() / 2); // number of bytes in data field
-        String response = mTelephonyManager.sendEnvelopeWithStatus(
-                CLA_ENVELOPE
-                + COMMAND_ENVELOPE
-                + "00" // p1: value required for Envelope command
-                + "00" // p2: value required for Envelope command
-                + lc
-                + envelope);
-        assertEquals("sendEnvelopeWithStatus returned: " + response,
-                STATUS_NORMAL_STRING, response);
+        String response = mTelephonyManager.sendEnvelopeWithStatus(envelope);
+
+        // TODO(b/137963715): add more specific assertions on response from TelMan#sendEnvelope
+        assertNotNull("sendEnvelopeWithStatus is null for envelope=" + envelope, response);
     }
 
     private void verifyValidIccOpenLogicalChannelResponse(IccOpenLogicalChannelResponse response) {
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index bbba7a1..a2025bd 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -2111,7 +2111,7 @@
     public void testWrapHardwareBufferHoldsReference() {
         Bitmap bitmap;
         // Create hardware-buffer and wrap it in a Bitmap
-        try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
+        try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
             // Fill buffer with colors (x, y, 42, 255)
             nFillRgbaHwBuffer(hwBuffer);
             bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
diff --git a/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java b/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java
new file mode 100644
index 0000000..c455751
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 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.location.cts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.Settings;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.AndroidTestCase;
+
+import com.android.compatibility.common.util.CddTest;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests if system settings app provides scanning settings.
+ */
+public class ScanningSettingsTest extends AndroidTestCase {
+    private static final String TAG = "ScanningSettingsTest";
+
+    private static final int TIMEOUT = 8_000;  // 8 seconds
+    private static final String SETTINGS_PACKAGE = "com.android.settings";
+
+    private static final String WIFI_SCANNING_TITLE_RES =
+            "location_scanning_wifi_always_scanning_title";
+    private static final String BLUETOOTH_SCANNING_TITLE_RES =
+            "location_scanning_bluetooth_always_scanning_title";
+
+    private UiDevice mDevice;
+    private Context mContext;
+    private String mLauncherPackage;
+    private PackageManager mPackageManager;
+
+    @Override
+    protected void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+        mPackageManager = mContext.getPackageManager();
+        final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+        launcherIntent.addCategory(Intent.CATEGORY_HOME);
+        mLauncherPackage = mPackageManager.resolveActivity(launcherIntent,
+                PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
+    }
+
+    @CddTest(requirement = "7.4.2/C-2-1")
+    public void testWifiScanningSettings() throws PackageManager.NameNotFoundException {
+        launchScanningSettings();
+        toggleSettingAndVerify(WIFI_SCANNING_TITLE_RES, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
+    }
+
+    @CddTest(requirement = "7.4.3/C-4-1")
+    public void testBleScanningSettings() throws PackageManager.NameNotFoundException {
+        launchScanningSettings();
+        toggleSettingAndVerify(BLUETOOTH_SCANNING_TITLE_RES,
+                Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE);
+    }
+
+    private void launchScanningSettings() {
+        // Start from the home screen
+        mDevice.pressHome();
+        mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
+
+        final Intent intent = new Intent(Settings.ACTION_LOCATION_SCANNING_SETTINGS);
+        // Clear out any previous instances
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(intent);
+
+        // Wait for the app to appear
+        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+    }
+
+    private void clickAndWaitForSettingChange(UiObject2 pref, ContentResolver resolver,
+            String settingKey) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        final ContentObserver observer = new ContentObserver(
+                new Handler(handlerThread.getLooper())) {
+            @Override
+            public void onChange(boolean selfChange) {
+                super.onChange(selfChange);
+                latch.countDown();
+            }
+        };
+        resolver.registerContentObserver(Settings.Global.getUriFor(settingKey), false, observer);
+        pref.click();
+        try {
+            latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        handlerThread.quit();
+        resolver.unregisterContentObserver(observer);
+        assertEquals(0, latch.getCount());
+    }
+
+    private void toggleSettingAndVerify(String prefTitleRes, String settingKey)
+            throws PackageManager.NameNotFoundException {
+        final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE);
+        final int resId = res.getIdentifier(prefTitleRes, "string", SETTINGS_PACKAGE);
+        final UiObject2 pref = mDevice.findObject(By.text(res.getString(resId)));
+        final ContentResolver resolver = mContext.getContentResolver();
+        final boolean checked = Settings.Global.getInt(resolver, settingKey, 0) == 1;
+
+        // Click the preference to toggle the setting.
+        clickAndWaitForSettingChange(pref, resolver, settingKey);
+        assertEquals(!checked, Settings.Global.getInt(resolver, settingKey, 0) == 1);
+
+        // Click the preference again to toggle the setting back.
+        clickAndWaitForSettingChange(pref, resolver, settingKey);
+        assertEquals(checked, Settings.Global.getInt(resolver, settingKey, 0) == 1);
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index 232cd1f..e9b639f 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -34,10 +34,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
+import android.app.ActivityManager;
 import android.app.UiAutomation;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -108,6 +110,8 @@
     private static final long BACKGROUND_ACCESS_SETTLE_TIME = 11000;
 
     private static final Context sContext = InstrumentationRegistry.getTargetContext();
+    private static final ActivityManager sActivityManager =
+            (ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE);
     private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation()
             .getUiAutomation();
 
@@ -361,6 +365,14 @@
     }
 
     /**
+     * Skip each test for low ram device
+     */
+    @Before
+    public void assumeIsNotLowRamDevice() {
+        assumeFalse(sActivityManager.isLowRamDevice());
+    }
+
+    /**
      * Reset the permission controllers state before each test
      */
     @Before
diff --git a/tests/tests/permission2/AndroidTest.xml b/tests/tests/permission2/AndroidTest.xml
index 9e78d64..bcab153 100644
--- a/tests/tests/permission2/AndroidTest.xml
+++ b/tests/tests/permission2/AndroidTest.xml
@@ -39,6 +39,12 @@
         <option name="push" value="CtsStoragePermissionsUserOptInSdk22.apk->/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk22.apk" />
         <option name="push" value="CtsStoragePermissionsUserOptInSdk28.apk->/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptInSdk28.apk" />
         <option name="push" value="CtsStoragePermissionsUserOptOutSdk29.apk->/data/local/tmp/cts/permissions2/CtsStoragePermissionsUserOptOutSdk29.apk" />
+        <option name="push" value="CtsLegacyStorageNotIsolatedWithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsLegacyStorageNotIsolatedWithSharedUid.apk" />
+        <option name="push" value="CtsLegacyStorageIsolatedWithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsLegacyStorageIsolatedWithSharedUid.apk" />
+        <option name="push" value="CtsLegacyStorageRestrictedWithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsLegacyStorageRestrictedWithSharedUid.apk" />
+        <option name="push" value="CtsLegacyStorageRestrictedSdk28WithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsLegacyStorageRestrictedSdk28WithSharedUid.apk" />
+        <option name="push" value="CtsSMSRestrictedWithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsSMSRestrictedWithSharedUid.apk" />
+        <option name="push" value="CtsSMSNotRestrictedWithSharedUid.apk->/data/local/tmp/cts/permissions2/CtsSMSNotRestrictedWithSharedUid.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
new file mode 100644
index 0000000..6ee6120
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsLegacyStorageIsolatedWithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..9054889
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.legacystoragewithshareduid.isolated"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application android:label="CtsLegacyStorageIsolatedWithSharedUid" />
+</manifest>
diff --git a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
new file mode 100644
index 0000000..63c2a14
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsLegacyStorageNotIsolatedWithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..179f19a
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.legacystoragewithshareduid.notisolated"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application android:label="CtsLegacyStorageNotIsolatedWithSharedUid"
+         android:requestLegacyExternalStorage="true" />
+</manifest>
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
new file mode 100644
index 0000000..50ac715
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsLegacyStorageRestrictedSdk28WithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..89cadcd
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.legacystoragewithshareduid.restrictedsdk28"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application android:label="CtsLegacyStorageRestrictedSdk28WithSharedUid" />
+</manifest>
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
new file mode 100644
index 0000000..78068f9
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsLegacyStorageRestrictedWithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..5791147
--- /dev/null
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.legacystoragewithshareduid.restricted"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application android:label="CtsLegacyStorageRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
new file mode 100644
index 0000000..9806571
--- /dev/null
+++ b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsSMSNotRestrictedWithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..83c43a2
--- /dev/null
+++ b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.smswithshareduid.notrestricted"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-permission android:name="android.permission.READ_SMS" />
+
+    <application android:label="CtsSMSNotRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
new file mode 100644
index 0000000..ec6d128
--- /dev/null
+++ b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_test_helper_app {
+    name: "CtsSMSRestrictedWithSharedUid",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 0000000..a497392
--- /dev/null
+++ b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission2.cts.smswithshareduid.restricted"
+    android:versionCode="1"
+    android:sharedUserId="android.permission2.cts.restrictedpermissionuser.shareduid">
+
+    <uses-permission android:name="android.permission.READ_SMS" />
+
+    <application android:label="CtsSMSRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
index 996630a..7dca607 100644
--- a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
@@ -16,6 +16,7 @@
 
 package android.permission2.cts;
 
+import static android.Manifest.permission.READ_SMS;
 import static android.permission.cts.PermissionUtils.eventually;
 import static android.permission.cts.PermissionUtils.isGranted;
 import static android.permission.cts.PermissionUtils.isPermissionGranted;
@@ -104,6 +105,18 @@
 
     private static final String PKG = "android.permission2.cts.restrictedpermissionuser";
 
+    private static final String APK_USES_SMS_RESTRICTED_SHARED_UID =
+            "/data/local/tmp/cts/permissions2/CtsSMSRestrictedWithSharedUid.apk";
+
+    private static final String PKG_USES_SMS_RESTRICTED_SHARED_UID =
+            "android.permission2.cts.smswithshareduid.restricted";
+
+    private static final String APK_USES_SMS_NOT_RESTRICTED_SHARED_UID =
+            "/data/local/tmp/cts/permissions2/CtsSMSNotRestrictedWithSharedUid.apk";
+
+    private static final String PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID =
+            "android.permission2.cts.smswithshareduid.notrestricted";
+
     private static final long UI_TIMEOUT = 5000L;
 
     private static @NonNull BroadcastReceiver sCommandReceiver;
@@ -681,6 +694,19 @@
         assertNoRestrictedPermissionWhitelisted();
     }
 
+    @Test
+    @AppModeFull
+    public void shareUidBetweenRestrictedAndNotRestrictedApp() throws Exception {
+        runShellCommand(
+                "pm install -g --restrict-permissions " + APK_USES_SMS_RESTRICTED_SHARED_UID);
+        runShellCommand("pm install -g " + APK_USES_SMS_NOT_RESTRICTED_SHARED_UID);
+
+        eventually(
+                () -> assertThat(isGranted(PKG_USES_SMS_RESTRICTED_SHARED_UID, READ_SMS)).isTrue());
+        // The apps share a UID, hence the whitelisting is shared too
+        assertThat(isGranted(PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID, READ_SMS)).isTrue();
+    }
+
     private static void installRestrictedPermissionUserApp(@NonNull SessionParams params)
             throws Exception {
         final CountDownLatch installLatch = new CountDownLatch(1);
@@ -1113,6 +1139,8 @@
     @After
     public void uninstallApp() {
         runShellCommand("pm uninstall " + PKG);
+        runShellCommand("pm uninstall " + PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID);
+        runShellCommand("pm uninstall " + PKG_USES_SMS_RESTRICTED_SHARED_UID);
     }
 
     private static @NonNull Context getContext() {
diff --git a/tests/tests/permission2/src/android/permission2/cts/RestrictedStoragePermissionSharedUidTest.java b/tests/tests/permission2/src/android/permission2/cts/RestrictedStoragePermissionSharedUidTest.java
new file mode 100644
index 0000000..1d0ded2
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/RestrictedStoragePermissionSharedUidTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 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.permission2.cts;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
+import static android.permission.cts.PermissionUtils.eventually;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission2.cts.RestrictedStoragePermissionSharedUidTest.StorageState.DENIED;
+import static android.permission2.cts.RestrictedStoragePermissionSharedUidTest.StorageState.ISOLATED;
+import static android.permission2.cts.RestrictedStoragePermissionSharedUidTest.StorageState.NON_ISOLATED;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.lang.Integer.min;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+
+@AppModeFull(reason = "Instant apps cannot access other app's properties")
+@RunWith(Parameterized.class)
+public class RestrictedStoragePermissionSharedUidTest {
+    private static final String LOG_TAG =
+            RestrictedStoragePermissionSharedUidTest.class.getSimpleName();
+
+    public enum StorageState {
+        /** The app has non-isolated storage */
+        NON_ISOLATED,
+
+        /** The app has isolated storage */
+        ISOLATED,
+
+        /** The read-external-storage permission cannot be granted */
+        DENIED
+    }
+
+    /**
+     * An app that is tested
+     */
+    private static class TestApp {
+        private static @NonNull Context sContext =
+                InstrumentationRegistry.getInstrumentation().getContext();
+        private static @NonNull AppOpsManager sAppOpsManager =
+                sContext.getSystemService(AppOpsManager.class);
+        private static @NonNull PackageManager sPackageManager = sContext.getPackageManager();
+
+        private final String mApk;
+        private final String mPkg;
+
+        public final boolean isRestricted;
+        public final boolean hasRequestedLegacyExternalStorage;
+
+        TestApp(@NonNull String apk, @NonNull String pkg, boolean isRestricted,
+                @NonNull boolean hasRequestedLegacyExternalStorage) {
+            mApk = apk;
+            mPkg = pkg;
+
+            this.isRestricted = isRestricted;
+            this.hasRequestedLegacyExternalStorage = hasRequestedLegacyExternalStorage;
+        }
+
+        /**
+         * Assert that the read-external-storage permission was granted or not granted.
+         *
+         * @param expectGranted {@code true} if the permission is expected to be granted
+         */
+        void assertStoragePermGranted(boolean expectGranted) {
+            eventually(() -> assertThat(isGranted(mPkg, READ_EXTERNAL_STORAGE)).named(
+                    this + " read storage granted").isEqualTo(expectGranted));
+        }
+
+        /**
+         * Assert that the app has non-isolated storage
+         *
+         * @param expectGranted {@code true} if the app is expected to have non-isolated storage
+         */
+        void assertHasNotIsolatedStorage(boolean expectHasNotIsolatedStorage) {
+            eventually(() -> runWithShellPermissionIdentity(() -> {
+                int uid = sContext.getPackageManager().getPackageUid(mPkg, 0);
+                if (expectHasNotIsolatedStorage) {
+                    assertThat(sAppOpsManager.unsafeCheckOpRawNoThrow(OPSTR_LEGACY_STORAGE, uid,
+                            mPkg)).named(this + " legacy storage mode").isEqualTo(MODE_ALLOWED);
+                } else {
+                    assertThat(sAppOpsManager.unsafeCheckOpRawNoThrow(OPSTR_LEGACY_STORAGE, uid,
+                            mPkg)).named(this + " legacy storage mode").isNotEqualTo(MODE_ALLOWED);
+                }
+            }));
+        }
+
+        int getTargetSDK() throws Exception {
+            return sPackageManager.getApplicationInfo(mPkg, 0).targetSdkVersion;
+        }
+
+        void install() {
+            if (isRestricted) {
+                runShellCommand("pm install -g --restrict-permissions " + mApk);
+            } else {
+                runShellCommand("pm install -g " + mApk);
+            }
+        }
+
+        void uninstall() {
+            runShellCommand("pm uninstall " + mPkg);
+        }
+
+        @Override
+        public String toString() {
+            return mPkg.substring(PKG_PREFIX.length());
+        }
+    }
+
+    /**
+     * Placeholder for "no app". The properties are chosen that when combined with another app, the
+     * other app always decides the resulting property,
+     */
+    private static class NoApp extends TestApp {
+        NoApp() {
+            super("", PKG_PREFIX + "(none)", true, false);
+        }
+
+        void assertStoragePermGranted(boolean ignored) {
+            // empty
+        }
+
+        void assertHasNotIsolatedStorage(boolean ignored) {
+            // empty
+        }
+
+        @Override
+        int getTargetSDK() {
+            return 10000;
+        }
+
+        @Override
+        public void install() {
+            // empty
+        }
+
+        @Override
+        public void uninstall() {
+            // empty
+        }
+    }
+
+    private static final String APK_PATH = "/data/local/tmp/cts/permissions2/";
+    private static final String PKG_PREFIX = "android.permission2.cts.legacystoragewithshareduid.";
+
+    private static final TestApp[] TEST_APPS = new TestApp[]{
+            new TestApp(APK_PATH + "CtsLegacyStorageNotIsolatedWithSharedUid.apk",
+                    PKG_PREFIX + "notisolated", false, true),
+            new TestApp(APK_PATH + "CtsLegacyStorageIsolatedWithSharedUid.apk",
+                    PKG_PREFIX + "isolated", false, false),
+            new TestApp(APK_PATH + "CtsLegacyStorageRestrictedWithSharedUid.apk",
+                    PKG_PREFIX + "restricted", true, false),
+            new TestApp(APK_PATH + "CtsLegacyStorageRestrictedSdk28WithSharedUid.apk",
+                    PKG_PREFIX + "restrictedsdk28", true, true),
+            new NoApp()};
+
+    /**
+     * First app to be tested. This is the first in an entry created by {@link
+     * #getTestAppCombinations}
+     */
+    @Parameter(0)
+    public @NonNull TestApp app1;
+
+    /**
+     * Second app to be tested. This is the second in an entry created by {@link
+     * #getTestAppCombinations}
+     */
+    @Parameter(1)
+    public @NonNull TestApp app2;
+
+    /**
+     * Run this test for all combination of two tests-apps out of {@link #TEST_APPS}. This includes
+     * the {@link NoApp}, i.e. we also test a single test-app by itself.
+     *
+     * @return All combinations of two test-apps
+     */
+    @Parameters(name = "{0} and {1}")
+    public static Iterable<Object[]> getTestAppCombinations() {
+        ArrayList<Object[]> parameters = new ArrayList<>();
+
+        for (int firstApp = 0; firstApp < TEST_APPS.length; firstApp++) {
+            for (int secondApp = firstApp + 1; secondApp < TEST_APPS.length; secondApp++) {
+                parameters.add(new Object[]{TEST_APPS[firstApp], TEST_APPS[secondApp]});
+            }
+        }
+
+        return parameters;
+    }
+
+    @Test
+    public void checkExceptedStorageStateForAppsSharingUid() throws Exception {
+        app1.install();
+        app2.install();
+
+        int targetSDK = min(app1.getTargetSDK(), app2.getTargetSDK());
+        boolean isRestricted = app1.isRestricted && app2.isRestricted;
+        boolean hasRequestedLegacyExternalStorage =
+                app1.hasRequestedLegacyExternalStorage || app2.hasRequestedLegacyExternalStorage;
+
+        StorageState expectedState;
+        if (isRestricted) {
+            if (targetSDK < Build.VERSION_CODES.Q) {
+                expectedState = DENIED;
+            } else {
+                expectedState = ISOLATED;
+            }
+        } else if (hasRequestedLegacyExternalStorage) {
+            expectedState = NON_ISOLATED;
+        } else {
+            expectedState = ISOLATED;
+        }
+
+        Log.i(LOG_TAG, "Expected state=" + expectedState);
+
+        app1.assertStoragePermGranted(expectedState != DENIED);
+        app2.assertStoragePermGranted(expectedState != DENIED);
+
+        if (expectedState != DENIED) {
+            app1.assertHasNotIsolatedStorage(expectedState == NON_ISOLATED);
+            app2.assertHasNotIsolatedStorage(expectedState == NON_ISOLATED);
+        }
+    }
+
+    @After
+    public void uninstallAllTestPackages() {
+        app1.uninstall();
+        app2.uninstall();
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
index 78df1a9..4a8afe7 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
@@ -168,8 +168,9 @@
     @Test
     public void testCanonicalize() throws Exception {
         // Remove all audio left over from other tests
-        ProviderTestUtils.executeShellCommand(
-                "content delete --uri " + mExternalAudio,
+        ProviderTestUtils.executeShellCommand("content delete"
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --uri " + mExternalAudio,
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
 
         // Publish some content
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index 177fddb..a343c76 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -229,7 +229,9 @@
         cleanExternalMediaFile(externalPath2);
 
         int numBytes = 1337;
-        FileUtils.createFile(new File(externalPath), numBytes);
+        File file = new File(externalPath);
+        FileUtils.createFile(file, numBytes);
+        ProviderTestUtils.waitUntilExists(file);
 
         ContentValues values = new ContentValues();
         values.put(Media.ORIENTATION, 0);
@@ -281,7 +283,7 @@
         } finally {
             // delete
             assertEquals(1, mContentResolver.delete(uri, null, null));
-            new File(externalPath).delete();
+            file.delete();
         }
     }
 
@@ -342,8 +344,9 @@
         }
 
         // Now remove ownership, which means that Exif/XMP location data should be redacted
-        ProviderTestUtils.executeShellCommand(
-                "content update --uri " + publishUri + " --bind owner_package_name:n:",
+        ProviderTestUtils.executeShellCommand("content update"
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --uri " + publishUri + " --bind owner_package_name:n:",
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
         try (InputStream is = mContentResolver.openInputStream(publishUri)) {
             final ExifInterface exif = new ExifInterface(is);
@@ -405,8 +408,9 @@
     @Test
     public void testCanonicalize() throws Exception {
         // Remove all audio left over from other tests
-        ProviderTestUtils.executeShellCommand(
-                "content delete --uri " + mExternalImages,
+        ProviderTestUtils.executeShellCommand("content delete"
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --uri " + mExternalImages,
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
 
         // Publish some content
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index e30d604..6a7a1f9 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -250,8 +250,9 @@
         }
 
         // Now remove ownership, which means that location should be redacted
-        ProviderTestUtils.executeShellCommand(
-                "content update --uri " + publishUri + " --bind owner_package_name:n:",
+        ProviderTestUtils.executeShellCommand("content update"
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --uri " + publishUri + " --bind owner_package_name:n:",
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
         try (ParcelFileDescriptor pfd = mContentResolver.openFile(publishUri, "r", null);
                 MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
@@ -316,8 +317,9 @@
     @Test
     public void testCanonicalize() throws Exception {
         // Remove all audio left over from other tests
-        ProviderTestUtils.executeShellCommand(
-                "content delete --uri " + mExternalVideo,
+        ProviderTestUtils.executeShellCommand("content delete"
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --uri " + mExternalVideo,
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
 
         // Publish some content
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index e748360..b2db009 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -18,6 +18,7 @@
 
 import static android.provider.cts.MediaStoreTest.TAG;
 
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assert.fail;
 
 import android.app.UiAutomation;
@@ -28,6 +29,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
 import android.provider.cts.MediaStoreUtils.PendingParams;
@@ -39,6 +41,8 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.Timeout;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -68,6 +72,8 @@
     private static final Pattern PATTERN_STORAGE_PATH = Pattern.compile(
             "(?i)^/storage/[^/]+/(?:[0-9]+/)?");
 
+    private static final Timeout IO_TIMEOUT = new Timeout("IO_TIMEOUT", 2_000, 2, 2_000);
+
     static Iterable<String> getSharedVolumeNames() {
         // We test both new and legacy volume names
         final HashSet<String> testVolumes = new HashSet<>();
@@ -175,12 +181,29 @@
         executeShellCommand("bmgr wipe " + backupTransport + " " + packageName, uiAutomation);
     }
 
+    /**
+     * Waits until a file exists, or fails.
+     *
+     * @return existing file.
+     */
+    public static File waitUntilExists(File file) throws IOException {
+        try {
+            return IO_TIMEOUT.run("file '" + file + "' doesn't exist yet", () -> {
+                return file.exists() ? file : null; // will retry if it returns null
+            });
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
     static File stageDir(String volumeName) throws IOException {
         if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
             volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
         }
-        return Environment.buildPath(MediaStore.getVolumePath(volumeName), "Android", "media",
+        File dir = Environment.buildPath(MediaStore.getVolumePath(volumeName), "Android", "media",
                 "android.provider.cts");
+        Log.d(TAG, "stageDir(" + volumeName + "): returning " + dir);
+        return dir;
     }
 
     static File stageDownloadDir(String volumeName) throws IOException {
@@ -221,7 +244,7 @@
                 FileUtils.copy(source, target);
             }
         }
-        return file;
+        return waitUntilExists(file);
     }
 
     static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
@@ -243,11 +266,15 @@
     }
 
     static Uri scanFile(File file) throws Exception {
-        return MediaStore.scanFile(InstrumentationRegistry.getTargetContext(), file);
+        Uri uri = MediaStore.scanFile(InstrumentationRegistry.getTargetContext(), file);
+        assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
+        return uri;
     }
 
     static Uri scanFileFromShell(File file) throws Exception {
-        return MediaStore.scanFileFromShell(InstrumentationRegistry.getTargetContext(), file);
+        Uri uri = MediaStore.scanFileFromShell(InstrumentationRegistry.getTargetContext(), file);
+        assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
+        return uri;
     }
 
     static void scanVolume(File file) throws Exception {
@@ -321,8 +348,9 @@
     }
 
     public static File getRawFile(Uri uri) throws Exception {
-        final String res = ProviderTestUtils.executeShellCommand(
-                "content query --uri " + uri + " --projection _data",
+        final String res = ProviderTestUtils.executeShellCommand("content query --uri " + uri
+                + " --user " + InstrumentationRegistry.getTargetContext().getUserId()
+                + " --projection _data",
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
         final int i = res.indexOf("_data=");
         if (i >= 0) {
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
index 3327390..ef8011c 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
@@ -17,6 +17,7 @@
 package android.provider.cts;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,8 @@
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
 
+import com.android.compatibility.common.util.RequiredServiceRule;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -47,11 +50,11 @@
 
     private static final int TIMEOUT = 8000;
 
-    private static final String SETTINGS_PACKAGE = "com.android.settings";
     private static final String RESOURCE_DONE = "done";
     private static final String RESOURCE_SEE_MORE = "see_more";
     private static final String RESOURCE_TITLE = "panel_title";
 
+    private String mSettingsPackage = "com.android.settings";
     private String mLauncherPackage;
 
     private Context mContext;
@@ -68,6 +71,10 @@
         launcherIntent.addCategory(Intent.CATEGORY_HOME);
         mLauncherPackage = packageManager.resolveActivity(launcherIntent,
                 PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
+
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            mSettingsPackage = "com.android.car.settings";
+        }
     }
 
     @After
@@ -84,7 +91,7 @@
 
         String currentPackage = mDevice.getCurrentPackageName();
 
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -93,7 +100,7 @@
 
         String currentPackage = mDevice.getCurrentPackageName();
 
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -102,7 +109,7 @@
 
         String currentPackage = mDevice.getCurrentPackageName();
 
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -111,14 +118,14 @@
 
         String currentPackage = mDevice.getCurrentPackageName();
 
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
     }
 
     @Test
     public void internetPanel_correctTitle() {
         launchInternetPanel();
 
-        final UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        final UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
 
         assertThat(titleView.getText()).isEqualTo("Internet Connectivity");
     }
@@ -127,7 +134,7 @@
     public void volumePanel_correctTitle() {
         launchVolumePanel();
 
-        final UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        final UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
 
         assertThat(titleView.getText()).isEqualTo("Volume");
     }
@@ -136,7 +143,7 @@
     public void nfcPanel_correctTitle() {
         launchNfcPanel();
 
-        final UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        final UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
 
         assertThat(titleView.getText()).isEqualTo("NFC");
     }
@@ -145,7 +152,7 @@
     public void wifiPanel_correctTitle() {
         launchWifiPanel();
 
-        final UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        final UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
 
         assertThat(titleView.getText()).isEqualTo("Wi\u2011Fi");
     }
@@ -155,15 +162,15 @@
         // Launch panel
         launchInternetPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the done button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_DONE)).click();
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_DONE)).click();
         mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
 
         // Assert that we have left the panel
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isNotEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isNotEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -171,15 +178,15 @@
         // Launch panel
         launchVolumePanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the done button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_DONE)).click();
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_DONE)).click();
         mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
 
         // Assert that we have left the panel
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isNotEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isNotEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -187,15 +194,15 @@
         // Launch panel
         launchNfcPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the done button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_DONE)).click();
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_DONE)).click();
         mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
 
         // Assert that we have left the panel
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isNotEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isNotEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -203,15 +210,15 @@
         // Launch panel
         launchWifiPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the done button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_DONE)).click();
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_DONE)).click();
         mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
 
         // Assert that we have left the panel
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isNotEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isNotEqualTo(mSettingsPackage);
     }
 
     @Test
@@ -219,16 +226,16 @@
         // Launch panel
         launchInternetPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the see more button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_SEE_MORE)).click();
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_SEE_MORE)).click();
+        mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
 
         // Assert that we're still in Settings, on a different page.
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
-        UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
+        UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
         assertThat(titleView).isNull();
     }
 
@@ -237,16 +244,16 @@
         // Launch panel
         launchVolumePanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the see more button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_SEE_MORE)).click();
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_SEE_MORE)).click();
+        mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
 
         // Assert that we're still in Settings, on a different page.
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
-        UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
+        UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
         assertThat(titleView).isNull();
     }
 
@@ -255,16 +262,16 @@
         // Launch panel
         launchNfcPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the see more button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_SEE_MORE)).click();
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_SEE_MORE)).click();
+        mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
 
         // Assert that we're still in Settings, on a different page.
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
-        UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
+        UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
         assertThat(titleView).isNull();
     }
 
@@ -273,16 +280,16 @@
         // Launch panel
         launchWifiPanel();
         String currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
 
         // Click the see more button
-        mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_SEE_MORE)).click();
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+        mDevice.findObject(By.res(mSettingsPackage, RESOURCE_SEE_MORE)).click();
+        mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
 
         // Assert that we're still in Settings, on a different page.
         currentPackage = mDevice.getCurrentPackageName();
-        assertThat(currentPackage).isEqualTo(SETTINGS_PACKAGE);
-        UiObject2 titleView = mDevice.findObject(By.res(SETTINGS_PACKAGE, RESOURCE_TITLE));
+        assertThat(currentPackage).isEqualTo(mSettingsPackage);
+        UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_TITLE));
         assertThat(titleView).isNull();
     }
 
@@ -295,6 +302,7 @@
     }
 
     private void launchNfcPanel() {
+        assumeTrue("device does not support NFC", RequiredServiceRule.hasService("nfc"));
         launchPanel(Settings.Panel.ACTION_NFC);
     }
 
@@ -313,6 +321,6 @@
         mContext.startActivity(intent);
 
         // Wait for the app to appear
-        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+        mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
     }
 }
diff --git a/tests/tests/resolverservice/Android.bp b/tests/tests/resolverservice/Android.bp
new file mode 100644
index 0000000..36bac13
--- /dev/null
+++ b/tests/tests/resolverservice/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 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.
+
+android_test {
+    name: "CtsResolverServiceTestCases",
+    sdk_version: "system_current",
+
+    srcs: [
+        "src/**/*.java"
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "ctstestrunner-axt",
+        "truth-prebuilt"
+    ],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ]
+}
diff --git a/tests/tests/resolverservice/AndroidManifest.xml b/tests/tests/resolverservice/AndroidManifest.xml
new file mode 100644
index 0000000..d52f3db
--- /dev/null
+++ b/tests/tests/resolverservice/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.service.resolver.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.service.resolver.cts"
+        android:label="CTS tests of android.service.resolver">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/resolverservice/AndroidTest.xml b/tests/tests/resolverservice/AndroidTest.xml
new file mode 100644
index 0000000..ee0cc8c
--- /dev/null
+++ b/tests/tests/resolverservice/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<configuration description="Config for CTS resolver service test cases">
+
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsResolverServiceTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.service.resolver.cts" />
+        <option name="runtime-hint" value="5m" />
+    </test>
+</configuration>
diff --git a/tests/tests/resolverservice/OWNERS b/tests/tests/resolverservice/OWNERS
new file mode 100644
index 0000000..4b9c5eb
--- /dev/null
+++ b/tests/tests/resolverservice/OWNERS
@@ -0,0 +1,4 @@
+ # Bug component: 24950
+kanlig@google.com
+patb@google.com
+chiuwinson@google.com
\ No newline at end of file
diff --git a/tests/tests/resolverservice/src/android/service/resolver/cts/ResolverTargetTest.java b/tests/tests/resolverservice/src/android/service/resolver/cts/ResolverTargetTest.java
new file mode 100644
index 0000000..015f877
--- /dev/null
+++ b/tests/tests/resolverservice/src/android/service/resolver/cts/ResolverTargetTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 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.service.resolver.cts;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.service.resolver.ResolverTarget;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link ResolverTarget}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ResolverTargetTest {
+
+    @Test
+    public void sanityCheckDataConsistency() throws Exception {
+        ResolverTarget target = new ResolverTarget();
+
+        target.setChooserScore(1.0f);
+        assertThat(target.getChooserScore(), is(1.0f));
+
+        target.setLaunchScore(0.5f);
+        assertThat(target.getLaunchScore(), is(0.5f));
+
+        target.setRecencyScore(0.3f);
+        assertThat(target.getRecencyScore(), is(0.3f));
+
+        target.setSelectProbability(0.2f);
+        assertThat(target.getSelectProbability(), is(0.2f));
+
+        target.setTimeSpentScore(0.1f);
+        assertThat(target.getTimeSpentScore(), is(0.1f));
+    }
+
+    @Test
+    public void sanityCheckParcelability() throws Exception {
+        ResolverTarget target = new ResolverTarget();
+
+        target.setChooserScore(1.0f);
+        target.setLaunchScore(0.5f);
+        target.setRecencyScore(0.3f);
+        target.setSelectProbability(0.2f);
+        target.setTimeSpentScore(0.1f);
+
+        Parcel parcel = Parcel.obtain();
+        target.writeToParcel(parcel, 0 /*flags*/);
+        parcel.setDataPosition(0);
+        ResolverTarget fromParcel = ResolverTarget.CREATOR.createFromParcel(parcel);
+
+        assertThat(fromParcel.getChooserScore(), is(1.0f));
+        assertThat(fromParcel.getLaunchScore(), is(0.5f));
+        assertThat(fromParcel.getRecencyScore(), is(0.3f));
+        assertThat(fromParcel.getSelectProbability(), is(0.2f));
+        assertThat(fromParcel.getTimeSpentScore(), is(0.1f));
+    }
+}
diff --git a/tests/tests/role/AndroidTest.xml b/tests/tests/role/AndroidTest.xml
index e309746..cf4b1d3 100644
--- a/tests/tests/role/AndroidTest.xml
+++ b/tests/tests/role/AndroidTest.xml
@@ -35,6 +35,7 @@
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsRoleTestApp.apk->/data/local/tmp/cts/role/CtsRoleTestApp.apk" />
+        <option name="push" value="CtsRoleTestApp28.apk->/data/local/tmp/cts/role/CtsRoleTestApp28.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
index 4ae5d5a..f1829f6 100644
--- a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
+++ b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
@@ -30,6 +30,14 @@
             android:name=".IsRoleHeldActivity"
             android:exported="true" />
 
+        <activity
+            android:name=".ChangeDefaultDialerActivity"
+            android:exported="true" />
+
+        <activity
+            android:name=".ChangeDefaultSmsActivity"
+            android:exported="true" />
+
         <!-- Dialer -->
         <activity android:name=".DialerDialActivity">
             <intent-filter>
diff --git a/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java
new file mode 100644
index 0000000..89cafa0
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.app.role.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default dialer app.
+ */
+public class ChangeDefaultDialerActivity extends Activity {
+
+    private static final int REQUEST_CODE_CHANGE_DEFAULT_DIALER = 1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
+                    .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+            startActivityForResult(intent, REQUEST_CODE_CHANGE_DEFAULT_DIALER);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_CHANGE_DEFAULT_DIALER) {
+            setResult(resultCode, data);
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+}
diff --git a/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java
new file mode 100644
index 0000000..00559bf
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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.app.role.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Telephony;
+
+/**
+ * An activity that tries to change the default SMS app.
+ */
+public class ChangeDefaultSmsActivity extends Activity {
+
+    private static final int REQUEST_CODE_CHANGE_DEFAULT_SMS = 1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
+                    .putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName);
+            startActivityForResult(intent, REQUEST_CODE_CHANGE_DEFAULT_SMS);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_CHANGE_DEFAULT_SMS) {
+            setResult(resultCode, data);
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+}
diff --git a/tests/tests/role/CtsRoleTestApp28/Android.bp b/tests/tests/role/CtsRoleTestApp28/Android.bp
new file mode 100644
index 0000000..0d89f4e
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp28/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 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.
+
+android_test {
+    name: "CtsRoleTestApp28",
+    sdk_version: "test_current",
+
+    srcs: [
+        "src/**/*.java"
+    ],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ]
+}
diff --git a/tests/tests/role/CtsRoleTestApp28/AndroidManifest.xml b/tests/tests/role/CtsRoleTestApp28/AndroidManifest.xml
new file mode 100644
index 0000000..8fd7012
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp28/AndroidManifest.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2019 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.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.app.role.cts.app28">
+
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
+
+    <application
+        android:label="CtsRoleTestApp28">
+
+        <activity
+            android:name=".ChangeDefaultDialerActivity"
+            android:exported="true" />
+
+        <activity
+            android:name=".ChangeDefaultSmsActivity"
+            android:exported="true" />
+
+        <!-- Dialer -->
+        <activity android:name=".DialerDialActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:scheme="tel" />
+            </intent-filter>
+        </activity>
+
+        <!-- Sms -->
+        <activity android:name=".SmsSendToActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SENDTO" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="smsto" />
+            </intent-filter>
+        </activity>
+        <service
+            android:name=".SmsRespondViaMessageService"
+            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
+            <intent-filter>
+                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="smsto" />
+            </intent-filter>
+        </service>
+        <receiver
+            android:name=".SmsDelieverReceiver"
+            android:permission="android.permission.BROADCAST_SMS">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_DELIVER" />
+            </intent-filter>
+        </receiver>
+        <receiver
+            android:name=".SmsWapPushDelieverReceiver"
+            android:permission="android.permission.BROADCAST_WAP_PUSH">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
+                <data android:mimeType="application/vnd.wap.mms-message" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java b/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java
new file mode 100644
index 0000000..5d1c47c
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.app.role.cts.app28;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default dialer app.
+ */
+public class ChangeDefaultDialerActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
+                    .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+            startActivity(intent);
+        }
+    }
+}
diff --git a/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java b/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java
new file mode 100644
index 0000000..37819bb
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.app.role.cts.app28;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default SMS app.
+ */
+public class ChangeDefaultSmsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
+                    .putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName);
+            startActivity(intent);
+        }
+    }
+}
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 736e501..0da6988 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -35,10 +35,12 @@
 import android.content.pm.PermissionInfo;
 import android.os.Process;
 import android.os.UserHandle;
+import android.provider.Telephony;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
+import android.telecom.TelecomManager;
 import android.util.Log;
 import android.util.Pair;
 
@@ -50,6 +52,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.AppOpsUtils;
+import com.android.compatibility.common.util.TestUtils;
 import com.android.compatibility.common.util.ThrowingRunnable;
 
 import org.junit.After;
@@ -91,6 +94,18 @@
             + ".extra.IS_ROLE_HELD";
     private static final String APP_REQUEST_ROLE_ACTIVITY_NAME = APP_PACKAGE_NAME
             + ".RequestRoleActivity";
+    private static final String APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_PACKAGE_NAME
+            + ".ChangeDefaultDialerActivity";
+    private static final String APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_PACKAGE_NAME
+            + ".ChangeDefaultSmsActivity";
+
+    private static final String APP_28_APK_PATH = "/data/local/tmp/cts/role/CtsRoleTestApp28.apk";
+    private static final String APP_28_PACKAGE_NAME = "android.app.role.cts.app28";
+    private static final String APP_28_LABEL = "CtsRoleTestApp28";
+    private static final String APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_28_PACKAGE_NAME
+            + ".ChangeDefaultDialerActivity";
+    private static final String APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_28_PACKAGE_NAME
+            + ".ChangeDefaultSmsActivity";
 
     private static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
             "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
@@ -107,7 +122,6 @@
             new ActivityTestRule<>(WaitForResultActivity.class);
 
     private String mRoleHolder;
-    private int mCurrentUserId;
 
     @Before
     public void saveRoleHolder() throws Exception {
@@ -133,13 +147,14 @@
 
     @Before
     public void installApp() throws Exception {
-        mCurrentUserId = Process.myUserHandle().getIdentifier();
         installPackage(APP_APK_PATH);
+        installPackage(APP_28_APK_PATH);
     }
 
     @After
     public void uninstallApp() throws Exception {
         uninstallPackage(APP_PACKAGE_NAME);
+        uninstallPackage(APP_28_PACKAGE_NAME);
     }
 
     @Before
@@ -354,15 +369,83 @@
     }
 
     private void clearPackageData(@NonNull String packageName) {
-        runShellCommand("pm clear --user " + mCurrentUserId + " " + packageName);
+        runShellCommand("pm clear --user " + Process.myUserHandle().getIdentifier() + " "
+                + packageName);
     }
 
     private void installPackage(@NonNull String apkPath) {
-        runShellCommand("pm install -r --user " + mCurrentUserId + " " + apkPath);
+        runShellCommand("pm install -r --user " + Process.myUserHandle().getIdentifier() + " "
+                + apkPath);
     }
 
     private void uninstallPackage(@NonNull String packageName) {
-        runShellCommand("pm uninstall --user " + mCurrentUserId + " " + packageName);
+        runShellCommand("pm uninstall --user " + Process.myUserHandle().getIdentifier() + " "
+                + packageName);
+    }
+
+    @Test
+    public void targetCurrentSdkAndChangeDefaultDialerThenIsCanceled() throws Exception {
+        WaitForResultActivity activity = mActivityRule.getActivity();
+        activity.startActivityToWaitForResult(new Intent()
+                .setComponent(new ComponentName(APP_PACKAGE_NAME,
+                        APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+        Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+        assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+    }
+
+    @Test
+    public void targetCurrentSdkAndChangeDefaultSmsThenIsCanceled() throws Exception {
+        WaitForResultActivity activity = mActivityRule.getActivity();
+        activity.startActivityToWaitForResult(new Intent()
+                .setComponent(new ComponentName(APP_PACKAGE_NAME,
+                        APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+        Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+        assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+    }
+
+    @FlakyTest
+    @Test
+    public void targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer() throws Exception {
+        sContext.startActivity(new Intent()
+                .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+                        APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        allowRoleRequestForApp28();
+        TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class);
+        TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals(
+                telecomManager.getDefaultDialerPackage(), APP_28_PACKAGE_NAME));
+    }
+
+    @FlakyTest
+    @Test
+    public void targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms() throws Exception {
+        sContext.startActivity(new Intent()
+                .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+                        APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        allowRoleRequestForApp28();
+        TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals(
+                Telephony.Sms.getDefaultSmsPackage(sContext), APP_28_PACKAGE_NAME));
+    }
+
+    private void allowRoleRequestForApp28() throws InterruptedException, IOException {
+        UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_28_LABEL)), TIMEOUT_MILLIS);
+        if (item == null) {
+            dumpWindowHierarchy();
+            fail("Cannot find item to click");
+        }
+        item.click();
+        UiObject2 button = sUiDevice.wait(Until.findObject(By.res("android:id/button1")),
+                TIMEOUT_MILLIS);
+        if (button == null) {
+            dumpWindowHierarchy();
+            fail("Cannot find button to click");
+        }
+        button.click();
     }
 
     @Test
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index f4ae8a7..3d4498d 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -27,6 +27,7 @@
     ctstestserver \
     ctstestrunner-axt \
     compatibility-device-util-axt \
+    compatibility-common-util-devicesidelib \
     guava \
     platform-test-annotations
 
diff --git a/tests/tests/security/res/raw/bug_23285192.mp3 b/tests/tests/security/res/raw/bug_23285192.mp3
new file mode 100644
index 0000000..b86e4d8
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_23285192.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_25928803.mp4 b/tests/tests/security/res/raw/bug_25928803.mp4
new file mode 100644
index 0000000..54d07d5
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_25928803.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_26399350_avc.mp4 b/tests/tests/security/res/raw/bug_26399350_avc.mp4
new file mode 100644
index 0000000..e6df897
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_26399350_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_36279112.mp4 b/tests/tests/security/res/raw/bug_36279112.mp4
new file mode 100644
index 0000000..1a970ff
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_36279112.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_37203196_framelen.mp4 b/tests/tests/security/res/raw/bug_37203196_framelen.mp4
new file mode 100644
index 0000000..223b756
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_37203196_framelen.mp4
@@ -0,0 +1,8 @@
+43
+3015
+2122
+1310
+1599
+4391
+3429
+15
diff --git a/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4 b/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4
new file mode 100644
index 0000000..1b59a7e
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4
new file mode 100644
index 0000000..261e173
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4
new file mode 100644
index 0000000..e8f1c41
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4
new file mode 100644
index 0000000..ecc10cb
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4
new file mode 100644
index 0000000..34851ad
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index b806e1b..4ac78e5 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -69,6 +69,7 @@
 import java.util.HashMap;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Pattern;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -76,6 +77,7 @@
 
 import android.security.cts.R;
 
+import android.security.NetworkSecurityPolicy;
 
 /**
  * Verify that the device is not vulnerable to any known Stagefright
@@ -556,6 +558,14 @@
         doStagefrightTest(R.raw.bug_32873375);
     }
 
+    @SecurityTest(minPatchLevel = "2018-02")
+    public void testStagefright_bug_63522067() throws Exception {
+        doStagefrightTestRawBlob(R.raw.bug_63522067_1_hevc, "video/hevc", 320, 420);
+        doStagefrightTestRawBlob(R.raw.bug_63522067_2_hevc, "video/hevc", 320, 420);
+        doStagefrightTestRawBlob(R.raw.bug_63522067_3_hevc, "video/hevc", 320, 420);
+        doStagefrightTestRawBlob(R.raw.bug_63522067_4_hevc, "video/hevc", 320, 420);
+    }
+
     @SecurityTest(minPatchLevel = "2016-03")
     public void testStagefright_bug_25765591() throws Exception {
         doStagefrightTest(R.raw.bug_25765591);
@@ -804,12 +814,40 @@
      before any existing test methods
      ***********************************************************/
 
+    @SecurityTest(minPatchLevel = "2017-07")
+    public void testStagefright_bug_36279112() throws Exception {
+        doStagefrightTest(R.raw.bug_36279112);
+    }
+
+    @SecurityTest(minPatchLevel = "2017-08")
+    public void testBug_37203196() throws Exception {
+        int[] frameSizes = getFrameSizes(R.raw.bug_37203196_framelen);
+        doStagefrightTestRawBlob(R.raw.bug_37203196_mpeg2, "video/mpeg2", 48, 48, frameSizes);
+    }
+
     @SecurityTest(minPatchLevel = "2018-06")
     public void testBug_73552574() throws Exception {
         int[] frameSizes = getFrameSizes(R.raw.bug_73552574_framelen);
         doStagefrightTestRawBlob(R.raw.bug_73552574_avc, "video/avc", 320, 240, frameSizes);
     }
 
+    @SecurityTest(minPatchLevel = "2015-09")
+    public void testStagefright_bug_23285192() throws Exception {
+        doStagefrightTest(R.raw.bug_23285192);
+    }
+
+    @SecurityTest(minPatchLevel = "2016-03")
+    public void testStagefright_bug_25928803() throws Exception {
+        doStagefrightTest(R.raw.bug_25928803);
+    }
+
+    @SecurityTest(minPatchLevel = "2016-04")
+    public void testBug_26399350() throws Exception {
+        int[] frameSizes = {657, 54930};
+        doStagefrightTestRawBlob(R.raw.bug_26399350_avc, "video/avc", 640, 480,
+                frameSizes);
+    }
+
     @SecurityTest(minPatchLevel = "2018-12")
     public void testBug_113260892() throws Exception {
         doStagefrightTestRawBlob(R.raw.bug_113260892_hevc, "video/hevc", 320, 240);
@@ -1056,6 +1094,8 @@
     }
 
     private void doStagefrightTest(final int rid) throws Exception {
+        NetworkSecurityPolicy policy = NetworkSecurityPolicy.getInstance();
+        policy.setCleartextTrafficPermitted(true);
         doStagefrightTestMediaPlayer(rid);
         doStagefrightTestMediaCodec(rid);
         doStagefrightTestMediaMetadataRetriever(rid);
@@ -1077,6 +1117,7 @@
         doStagefrightTestMediaPlayer(url);
         doStagefrightTestMediaCodec(url);
         doStagefrightTestMediaMetadataRetriever(url);
+        policy.setCleartextTrafficPermitted(false);
         server.shutdown();
     }
 
@@ -1153,8 +1194,23 @@
         MediaPlayer.OnPreparedListener,
         MediaPlayer.OnCompletionListener {
 
-        private final String[] validProcessNames = {
-            "mediaserver", "mediadrmserver", "media.extractor", "media.codec", "media.metrics"
+        private final Pattern[] validProcessPatterns = {
+            Pattern.compile("adsprpcd"),
+            Pattern.compile("android\\.hardware\\.cas@\\d+?\\.\\d+?-service"),
+            Pattern.compile("android\\.hardware\\.drm@\\d+?\\.\\d+?-service"),
+            Pattern.compile("android\\.hardware\\.drm@\\d+?\\.\\d+?-service\\.clearkey"),
+            Pattern.compile("android\\.hardware\\.drm@\\d+?\\.\\d+?-service\\.widevine"),
+            Pattern.compile("android\\.process\\.media"),
+            Pattern.compile("mediadrmserver"),
+            Pattern.compile("media\\.extractor"),
+            Pattern.compile("media\\.metrics"),
+            Pattern.compile("mediaserver"),
+            Pattern.compile("media\\.codec"),
+            Pattern.compile("media\\.swcodec"),
+            Pattern.compile("\\[?sdcard\\]?"), // name:/system/bin/sdcard, user:media_rw
+            // Match any vendor processes.
+            // It should only catch crashes that happen during the test.
+            Pattern.compile("vendor.*"),
         };
 
         @Override
@@ -1202,7 +1258,7 @@
                 if (crashes == null) {
                     Log.e(TAG, "Crash results not found for test " + getName());
                     return what;
-                } else if (CrashUtils.detectCrash(validProcessNames, true, crashes)) {
+                } else if (CrashUtils.securityCrashDetected(crashes, true, validProcessPatterns)) {
                     return what;
                 } else {
                     Log.i(TAG, "Crash ignored due to no security crash found for test " +
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
index 5b519b2..6bb72ec 100644
--- a/tests/tests/systemui/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -27,7 +27,9 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util-axt \
     ctstestrunner-axt \
+    cts-wm-util \
     androidx.test.rules \
     ub-uiautomator
 
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
index dbf3e76..9da89be 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
@@ -94,64 +94,6 @@
         }
     }
 
-    private boolean hasVirtualNavigationBar(ActivityTestRule<? extends LightBarBaseActivity> rule)
-            throws Throwable {
-        final WindowInsets[] inset = new WindowInsets[1];
-        rule.runOnUiThread(()-> {
-            inset[0] = rule.getActivity().getRootWindowInsets();
-        });
-        return inset[0].getStableInsetBottom() > 0;
-    }
-
-    private boolean isRunningInVr() {
-        final Context context = InstrumentationRegistry.getContext();
-        final Configuration config = context.getResources().getConfiguration();
-        return (config.uiMode & Configuration.UI_MODE_TYPE_MASK)
-                == Configuration.UI_MODE_TYPE_VR_HEADSET;
-    }
-
-    private void assumeBasics() {
-        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
-
-        // No bars on embedded devices.
-        assumeFalse(getInstrumentation().getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_EMBEDDED));
-
-        // No bars on TVs and watches.
-        // Automotive navigation bar is not transparent
-        assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
-                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
-                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
-                || pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
-
-
-        // Non-highEndGfx devices don't do colored system bars.
-        assumeTrue(ActivityManager.isHighEndGfx());
-    }
-
-    protected void assumeHasColoredStatusBar(ActivityTestRule<? extends LightBarBaseActivity> rule)
-            throws Throwable {
-        assumeBasics();
-
-        // No status bar when running in Vr
-        assumeFalse(isRunningInVr());
-
-        // Status bar exists only when top stable inset is positive
-        final WindowInsets[] inset = new WindowInsets[1];
-        rule.runOnUiThread(()-> {
-            inset[0] = rule.getActivity().getRootWindowInsets();
-        });
-        assumeTrue("Top stable inset is non-positive.", inset[0].getStableInsetTop() > 0);
-    }
-
-    protected void assumeHasColoredNavigationBar(
-            ActivityTestRule<? extends LightBarBaseActivity> rule) throws Throwable {
-        assumeBasics();
-
-        // No virtual navigation bar, so no effect.
-        assumeTrue(hasVirtualNavigationBar(rule));
-    }
-
     protected void checkNavigationBarDivider(LightBarBaseActivity activity, int dividerColor,
             int backgroundColor, String methodName) {
         final Bitmap bitmap = takeNavigationBarScreenshot(activity);
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index 95573d1..137c003 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -16,6 +16,9 @@
 
 package android.systemui.cts;
 
+import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar;
+import static android.server.wm.BarTestUtils.assumeHasColoredStatusBar;
+
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertTrue;
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java b/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
index f8036f2..7da28d0 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
@@ -16,6 +16,8 @@
 
 package android.systemui.cts;
 
+import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar;
+
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
index 7543c20..e4c6b07 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsActivity.java
@@ -43,7 +43,6 @@
     private static final int DISPLAY_CUTOUT_SLACK_DP = 20;
 
     private TextView mContent;
-    private boolean mIsSetViewBound;
     private WindowInsets mContentWindowInsets;
     private WindowInsets mDecorViewWindowInsets;
     private Rect mDecorBound;
@@ -164,25 +163,21 @@
      * To present the WindowInsets information to mContent.
      * To show all of results of getSystemWindowInsets(), getMandatorySytemGestureInsets(),
      * getSystemGestureInsets(), getTappableElementsInsets() and the exclude rects
+     *
+     * @param rect the rectangle want to add or pass into to setSystemGestureExclusionRects
      */
     @MainThread
-    public void setSystemGestureExclusion(boolean isSetViewBoundary) {
-        mIsSetViewBound = isSetViewBoundary;
+    public void setSystemGestureExclusion(Rect rect) {
         List<Rect> rects = new ArrayList<>();
-        if (mIsSetViewBound) {
-            rects.add(new Rect(0 /* content view full match activity's width*/,
-                    0 /* content view full match activity's height */,
-                    mContent.getWidth(),
-                    mContent.getHeight()));
+        if (rect != null) {
+            rects.add(rect);
         }
-
         getContentView().setSystemGestureExclusionRects(rects);
         showInfoInTextView();
     }
 
     private void showInfoInTextView() {
         StringBuilder sb = new StringBuilder();
-        sb.append(mIsSetViewBound ? "setSystemGestureExclusionRects" : "no set").append("\n");
         sb.append("exclude rect list = " + Arrays.deepToString(mContent
                 .getSystemGestureExclusionRects().toArray())).append("\n");
 
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
index 2ca4156..3645bae 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
@@ -16,22 +16,35 @@
 
 package android.systemui.cts;
 
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
+import static android.view.View.SYSTEM_UI_CLEARABLE_FLAGS;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.TestCase.fail;
 
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
+import android.provider.DeviceConfig;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
@@ -40,12 +53,19 @@
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.View;
+import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import com.google.common.collect.Lists;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -53,12 +73,12 @@
 import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 public class WindowInsetsBehaviorTests {
@@ -69,11 +89,17 @@
     private static final int STEPS = 10;
     private static final int DIP_INTERVAL = 40;
 
+    // The minimum value of the system gesture exclusion limit is 200 dp. The value here should be
+    // greater than that, so that we can test if the limit can be changed by DeviceConfig or not.
+    private static final int EXCLUSION_LIMIT_DP = 210;
+
     private final boolean mForceEnableGestureNavigation;
     private final Map<String, Boolean> mSystemGestureOptionsMap;
     private float mPixelsPerDp;
+    private int mDisplayWidth;
+    private int mExclusionLimit;
     private UiDevice mDevice;
-    private Rect mDragBound;
+    private Rect mSwipeBound;
     private String mEdgeToEdgeNavigationTitle;
     private String mSystemNavigationTitle;
     private String mGesturePreferenceTitle;
@@ -257,6 +283,8 @@
         final DisplayMetrics metrics = new DisplayMetrics();
         display.getRealMetrics(metrics);
         mPixelsPerDp = metrics.density;
+        mDisplayWidth = metrics.widthPixels;
+        mExclusionLimit = (int) (EXCLUSION_LIMIT_DP * mPixelsPerDp);
 
         // To setup the Edge to Edge environment by do the operation on Settings
         boolean isOperatedSettingsToExpectedOption = launchToSettingsSystemGesture();
@@ -284,7 +312,7 @@
         mActivity.setInitialFinishCallBack(isFinish -> latch.countDown());
         mDevice.waitForIdle();
 
-        latch.await(5, TimeUnit.SECONDS);
+        latch.await(5, SECONDS);
     }
 
     /**
@@ -311,8 +339,8 @@
     }
 
 
-    private void dragByUiDevice(Point p1, Point p2) {
-        mDevice.drag(p1.x, p1.y, p2.x, p2.y, STEPS);
+    private void swipeByUiDevice(Point p1, Point p2) {
+        mDevice.swipe(p1.x, p1.y, p2.x, p2.y, STEPS);
     }
 
     private void clickAndWaitByUiDevice(Point p) {
@@ -327,7 +355,7 @@
 
         /* wait until the OnClickListener triggered, and then click the next point */
         try {
-            latch.await(5, TimeUnit.SECONDS);
+            latch.await(5, SECONDS);
         } catch (InterruptedException e) {
             fail("Wait too long and onClickEvent doesn't receive");
         }
@@ -337,7 +365,7 @@
         }
     }
 
-    private int dragBigX(Rect viewBoundary, BiConsumer<Point, Point> callback) {
+    private int swipeBigX(Rect viewBoundary, BiConsumer<Point, Point> callback) {
         final int theLeftestLine = viewBoundary.left + 1;
         final int theToppestLine = viewBoundary.top + 1;
         final int theRightestLine = viewBoundary.right - 1;
@@ -395,7 +423,7 @@
         return count;
     }
 
-    private int dragAllOfHorizontalLinesFromLeftToRight(Rect viewBoundary,
+    private int swipeAllOfHorizontalLinesFromLeftToRight(Rect viewBoundary,
             BiConsumer<Point, Point> callback) {
         final int theLeftestLine = viewBoundary.left + 1;
         final int theToppestLine = viewBoundary.top + 1;
@@ -421,7 +449,7 @@
         return count;
     }
 
-    private int dragAllOfHorizontalLinesFromRightToLeft(Rect viewBoundary,
+    private int swipeAllOfHorizontalLinesFromRightToLeft(Rect viewBoundary,
             BiConsumer<Point, Point> callback) {
         final int theToppestLine = viewBoundary.top + 1;
         final int theRightestLine = viewBoundary.right - 1;
@@ -446,16 +474,16 @@
         return count;
     }
 
-    private int dragAllOfHorizontalLines(Rect viewBoundary, BiConsumer<Point, Point> callback) {
+    private int swipeAllOfHorizontalLines(Rect viewBoundary, BiConsumer<Point, Point> callback) {
         int count = 0;
 
-        count += dragAllOfHorizontalLinesFromLeftToRight(viewBoundary, callback);
-        count += dragAllOfHorizontalLinesFromRightToLeft(viewBoundary, callback);
+        count += swipeAllOfHorizontalLinesFromLeftToRight(viewBoundary, callback);
+        count += swipeAllOfHorizontalLinesFromRightToLeft(viewBoundary, callback);
 
         return count;
     }
 
-    private int dragAllOfVerticalLinesFromTopToBottom(Rect viewBoundary,
+    private int swipeAllOfVerticalLinesFromTopToBottom(Rect viewBoundary,
             BiConsumer<Point, Point> callback) {
         final int theLeftestLine = viewBoundary.left + 1;
         final int theToppestLine = viewBoundary.top + 1;
@@ -480,7 +508,7 @@
         return count;
     }
 
-    private int dragAllOfVerticalLinesFromBottomToTop(Rect viewBoundary,
+    private int swipeAllOfVerticalLinesFromBottomToTop(Rect viewBoundary,
             BiConsumer<Point, Point> callback) {
         final int theLeftestLine = viewBoundary.left + 1;
         final int theRightestLine = viewBoundary.right - 1;
@@ -505,40 +533,71 @@
         return count;
     }
 
-    private int dragAllOfVerticalLines(Rect viewBoundary, BiConsumer<Point, Point> callback) {
+    private int swipeAllOfVerticalLines(Rect viewBoundary, BiConsumer<Point, Point> callback) {
         int count = 0;
 
-        count += dragAllOfVerticalLinesFromTopToBottom(viewBoundary, callback);
-        count += dragAllOfVerticalLinesFromBottomToTop(viewBoundary, callback);
+        count += swipeAllOfVerticalLinesFromTopToBottom(viewBoundary, callback);
+        count += swipeAllOfVerticalLinesFromBottomToTop(viewBoundary, callback);
 
         return count;
     }
 
-    private int dragInViewBoundary(Rect viewBoundary, BiConsumer<Point, Point> callback) {
+    private int swipeInViewBoundary(Rect viewBoundary, BiConsumer<Point, Point> callback) {
         int count = 0;
 
-        count += dragBigX(viewBoundary, callback);
-        count += dragAllOfHorizontalLines(viewBoundary, callback);
-        count += dragAllOfVerticalLines(viewBoundary, callback);
+        count += swipeBigX(viewBoundary, callback);
+        count += swipeAllOfHorizontalLines(viewBoundary, callback);
+        count += swipeAllOfVerticalLines(viewBoundary, callback);
 
         return count;
     }
 
-    private int dragInViewBoundary(Rect viewBoundary) {
-        return dragInViewBoundary(viewBoundary, this::dragByUiDevice);
+    private int swipeInViewBoundary(Rect viewBoundary) {
+        return swipeInViewBoundary(viewBoundary, this::swipeByUiDevice);
+    }
+
+    private List<Rect> splitBoundsAccordingToExclusionLimit(Rect rect) {
+        final int exclusionHeightLimit = (int) (getPropertyOfMaxExclusionHeight() * mPixelsPerDp
+                + 0.5f);
+
+        final List<Rect> bounds = new ArrayList<>();
+        if (rect.height() < exclusionHeightLimit) {
+            bounds.add(rect);
+            return bounds;
+        }
+
+        int nextTop = rect.top;
+        while (nextTop >= rect.bottom) {
+            final int top = nextTop;
+            int bottom = top + exclusionHeightLimit;
+            if (bottom > rect.bottom) {
+                bottom = rect.bottom;
+            }
+
+            bounds.add(new Rect(rect.left, top, rect.right, bottom));
+
+            nextTop += bottom;
+        }
+
+        return bounds;
     }
 
     @Test
-    public void mandatorySystemGesture_excludeViewRects_withoutAnyCancel() {
+    public void mandatorySystemGesture_excludeViewRects_withoutAnyCancel()
+            throws InterruptedException {
         assumeTrue(hasSystemGestureFeature());
 
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(true));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+        mainThreadRun(() -> mSwipeBound = mActivity.getOperationArea(
                 mContentViewWindowInsets.getMandatorySystemGestureInsets(),
                 mContentViewWindowInsets));
 
-        int dragCount = dragInViewBoundary(mDragBound);
+        final List<Rect> swipeBounds = splitBoundsAccordingToExclusionLimit(mSwipeBound);
+        int swipeCount = 0;
+        for (Rect swipeBound : swipeBounds) {
+            setAndWaitForSystemGestureExclusionRectsListenerTrigger(swipeBound);
+            swipeCount += swipeInViewBoundary(swipeBound);
+        }
 
         mainThreadRun(() -> {
             mActionDownPoints = mActivity.getActionDownPoints();
@@ -548,20 +607,24 @@
         mScreenshotTestRule.capture();
 
         assertEquals(0, mActionCancelPoints.size());
-        assertEquals(dragCount, mActionUpPoints.size());
-        assertEquals(dragCount, mActionDownPoints.size());
+        assertEquals(swipeCount, mActionUpPoints.size());
+        assertEquals(swipeCount, mActionDownPoints.size());
     }
 
     @Test
     public void systemGesture_notExcludeViewRects_withoutAnyCancel() {
         assumeTrue(hasSystemGestureFeature());
 
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(false));
+        mainThreadRun(() -> mActivity.setSystemGestureExclusion(null));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+        mainThreadRun(() -> mSwipeBound = mActivity.getOperationArea(
                 mContentViewWindowInsets.getSystemGestureInsets(), mContentViewWindowInsets));
 
-        int dragCount = dragInViewBoundary(mDragBound);
+        final List<Rect> swipeBounds = splitBoundsAccordingToExclusionLimit(mSwipeBound);
+        int swipeCount = 0;
+        for (Rect swipeBound : swipeBounds) {
+            swipeCount += swipeInViewBoundary(swipeBound);
+        }
 
         mainThreadRun(() -> {
             mActionDownPoints = mActivity.getActionDownPoints();
@@ -571,20 +634,23 @@
         mScreenshotTestRule.capture();
 
         assertEquals(0, mActionCancelPoints.size());
-        assertEquals(dragCount, mActionUpPoints.size());
-        assertEquals(dragCount, mActionDownPoints.size());
+        assertEquals(swipeCount, mActionUpPoints.size());
+        assertEquals(swipeCount, mActionDownPoints.size());
     }
 
     @Test
-    public void tappableElements_tapSamplePoints_excludeViewRects_withoutAnyCancel() {
+    public void tappableElements_tapSamplePoints_excludeViewRects_withoutAnyCancel()
+            throws InterruptedException {
         assumeTrue(hasSystemGestureFeature());
 
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(true));
+        final Rect[] rects = new Rect[1];
+        mainThreadRun(() -> rects[0] = mActivity.getViewBound(mActivity.getContentView()));
+        setAndWaitForSystemGestureExclusionRectsListenerTrigger(rects[0]);
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+        mainThreadRun(() -> mSwipeBound = mActivity.getOperationArea(
                 mContentViewWindowInsets.getTappableElementInsets(), mContentViewWindowInsets));
 
-        int count = clickAllOfSamplePoints(mDragBound, this::clickAndWaitByUiDevice);
+        final int count = clickAllOfSamplePoints(mSwipeBound, this::clickAndWaitByUiDevice);
 
         mainThreadRun(() -> {
             mClickCount = mActivity.getClickCount();
@@ -601,12 +667,12 @@
     public void tappableElements_tapSamplePoints_notExcludeViewRects_withoutAnyCancel() {
         assumeTrue(hasSystemGestureFeature());
 
-        mainThreadRun(() -> mActivity.setSystemGestureExclusion(false));
+        mainThreadRun(() -> mActivity.setSystemGestureExclusion(null));
         mainThreadRun(() -> mContentViewWindowInsets = mActivity.getDecorViewWindowInsets());
-        mainThreadRun(() -> mDragBound = mActivity.getOperationArea(
+        mainThreadRun(() -> mSwipeBound = mActivity.getOperationArea(
                 mContentViewWindowInsets.getTappableElementInsets(), mContentViewWindowInsets));
 
-        int count = clickAllOfSamplePoints(mDragBound, this::clickAndWaitByUiDevice);
+        final int count = clickAllOfSamplePoints(mSwipeBound, this::clickAndWaitByUiDevice);
 
         mainThreadRun(() -> {
             mClickCount = mActivity.getClickCount();
@@ -618,4 +684,181 @@
         assertEquals("The Number of the canceled points not match", 0,
                 mActionCancelPoints.size());
     }
+
+    @Test
+    public void swipeInsideLimit_systemUiVisible_noEventCanceled() throws Throwable {
+        final int swipeCount = 1;
+        final boolean insideLimit = true;
+        testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_VISIBLE);
+
+        assertEquals("Swipe must not be canceled.", 0, mActionCancelPoints.size());
+        assertEquals("Action up points.", swipeCount, mActionUpPoints.size());
+        assertEquals("Action down points.", swipeCount, mActionDownPoints.size());
+    }
+
+    @Test
+    public void swipeOutsideLimit_systemUiVisible_allEventsCanceled() throws Throwable {
+        final int swipeCount = 1;
+        final boolean insideLimit = false;
+        testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_VISIBLE);
+
+        assertEquals("Swipe must be always canceled.", swipeCount, mActionCancelPoints.size());
+        assertEquals("Action up points.", 0, mActionUpPoints.size());
+        assertEquals("Action down points.", swipeCount, mActionDownPoints.size());
+    }
+
+    @Test
+    public void swipeInsideLimit_immersiveSticky_noEventCanceled() throws Throwable {
+        // The first event may be never canceled. So we need to swipe at least twice.
+        final int swipeCount = 2;
+        final boolean insideLimit = true;
+        testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+
+        assertEquals("Swipe must not be canceled.", 0, mActionCancelPoints.size());
+        assertEquals("Action up points.", swipeCount, mActionUpPoints.size());
+        assertEquals("Action down points.", swipeCount, mActionDownPoints.size());
+    }
+
+    @Test
+    public void swipeOutsideLimit_immersiveSticky_noEventCanceled() throws Throwable {
+        // The first event may be never canceled. So we need to swipe at least twice.
+        final int swipeCount = 2;
+        final boolean insideLimit = false;
+        testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+
+        assertEquals("Swipe must not be canceled.", 0, mActionCancelPoints.size());
+        assertEquals("Action up points.", swipeCount, mActionUpPoints.size());
+        assertEquals("Action down points.", swipeCount, mActionDownPoints.size());
+    }
+
+    private void testSystemGestureExclusionLimit(int swipeCount, boolean insideLimit,
+            int systemUiVisibility) throws Throwable {
+        final int shiftY = insideLimit ? 1 : -1;
+        assumeGestureNavigation();
+        doInExclusionLimitSession(() -> {
+            setSystemUiVisibility(systemUiVisibility);
+            setAndWaitForSystemGestureExclusionRectsListenerTrigger(null);
+
+            // The limit is consumed from bottom to top.
+            final int[] bottom = new int[1];
+            mainThreadRun(() -> {
+                final View rootView = mActivity.getWindow().getDecorView();
+                bottom[0] = rootView.getLocationOnScreen()[1] + rootView.getHeight();
+            });
+            final int swipeY = bottom[0] - mExclusionLimit + shiftY;
+
+            for (int i = 0; i < swipeCount; i++) {
+                swipeFromLeftToRight(swipeY, mDisplayWidth);
+            }
+
+            mainThreadRun(() -> {
+                mActionDownPoints = mActivity.getActionDownPoints();
+                mActionUpPoints = mActivity.getActionUpPoints();
+                mActionCancelPoints = mActivity.getActionCancelPoints();
+            });
+        });
+    }
+
+    private void assumeGestureNavigation() {
+        final Insets[] insets = new Insets[1];
+        mainThreadRun(() -> {
+            final View view = mActivity.getWindow().getDecorView();
+            insets[0] = view.getRootWindowInsets().getSystemGestureInsets();
+        });
+        assumeTrue("Gesture navigation required.", insets[0].left > 0);
+    }
+
+    /**
+     * Set system UI visibility and wait for it is applied by the system.
+     *
+     * @param flags the visibility flags.
+     * @throws InterruptedException when the test gets aborted.
+     */
+    private void setSystemUiVisibility(int flags) throws InterruptedException {
+        final CountDownLatch flagsApplied = new CountDownLatch(1);
+        final int targetFlags = SYSTEM_UI_CLEARABLE_FLAGS & flags;
+        mainThreadRun(() -> {
+            final View view = mActivity.getWindow().getDecorView();
+            if ((view.getSystemUiVisibility() & SYSTEM_UI_CLEARABLE_FLAGS) == targetFlags) {
+                // System UI visibility is already what we want. Stop waiting for the callback.
+                flagsApplied.countDown();
+                return;
+            }
+            view.setOnSystemUiVisibilityChangeListener(visibility -> {
+                if (visibility == targetFlags) {
+                    flagsApplied.countDown();
+                }
+            });
+            view.setSystemUiVisibility(flags);
+        });
+        assertTrue("System UI visibility must be applied.", flagsApplied.await(3, SECONDS));
+    }
+
+    /**
+     * Set an exclusion rectangle and wait for it is applied by the system.
+     * <p>
+     *     if the parameter rect doesn't provide or is null, the decorView will be used to set into
+     *     the exclusion rects.
+     * </p>
+     *
+     * @param rect the rectangle that is added into the system gesture exclusion rects.
+     * @throws InterruptedException when the test gets aborted.
+     */
+    private void setAndWaitForSystemGestureExclusionRectsListenerTrigger(Rect rect)
+            throws InterruptedException {
+        final CountDownLatch exclusionApplied = new CountDownLatch(1);
+        mainThreadRun(() -> {
+            final View view = mActivity.getWindow().getDecorView();
+            final ViewTreeObserver vto = view.getViewTreeObserver();
+            vto.addOnSystemGestureExclusionRectsChangedListener(
+                    rects -> exclusionApplied.countDown());
+            Rect exclusiveRect = new Rect(0, 0, view.getWidth(), view.getHeight());
+            if (rect != null) {
+                exclusiveRect = rect;
+            }
+            view.setSystemGestureExclusionRects(Lists.newArrayList(exclusiveRect));
+        });
+        assertTrue("Exclusion must be applied.", exclusionApplied.await(3, SECONDS));
+    }
+
+    private void swipeFromLeftToRight(int y, int distance) {
+        mDevice.swipe(0, y, distance, y, STEPS);
+    }
+
+    private static int getPropertyOfMaxExclusionHeight() {
+        final int[] originalLimitDp = new int[1];
+        SystemUtil.runWithShellPermissionIdentity(() -> {
+            originalLimitDp[0] = DeviceConfig.getInt(NAMESPACE_WINDOW_MANAGER,
+                    KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, -1);
+            DeviceConfig.setProperty(
+                    NAMESPACE_WINDOW_MANAGER,
+                    KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP,
+                    Integer.toString(EXCLUSION_LIMIT_DP), false /* makeDefault */);
+        });
+
+        return originalLimitDp[0];
+    }
+
+    /**
+     * Run the given task while the system gesture exclusion limit has been changed to
+     * {@link #EXCLUSION_LIMIT_DP}, and then restore the value while the task is finished.
+     *
+     * @param task the task to be run.
+     * @throws Throwable when something goes unexpectedly.
+     */
+    private static void doInExclusionLimitSession(ThrowingRunnable task) throws Throwable {
+        int originalLimitDp = getPropertyOfMaxExclusionHeight();
+        try {
+            task.run();
+        } finally {
+            // Restore the value
+            SystemUtil.runWithShellPermissionIdentity(() -> DeviceConfig.setProperty(
+                    NAMESPACE_WINDOW_MANAGER,
+                    KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP,
+                    (originalLimitDp != -1) ? Integer.toString(originalLimitDp) : null,
+                    false /* makeDefault */));
+        }
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
index 1abef13..6ae927b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
@@ -41,6 +41,8 @@
 import android.telecom.cts.redirectiontestapp.ICtsCallRedirectionServiceController;
 import android.text.TextUtils;
 
+import com.android.compatibility.common.util.CddTest;
+
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -194,6 +196,7 @@
     /**
      * Use RoleManager to query the previous call redirection app so we can restore it later.
      */
+    @CddTest(requirement ="3.2.3.5/C-2-5")
     private void rememberPreviousCallRedirectionApp() {
         runWithShellPermissionIdentity(() -> {
             List<String> callRedirectionApps = mRoleManager.getRoleHolders(ROLE_CALL_REDIRECTION);
@@ -205,6 +208,7 @@
         });
     }
 
+    @CddTest(requirement="3.2.3.5/C-2-4")
     private void addRoleHolder(String roleName, String packageName) throws Exception {
         UserHandle user = Process.myUserHandle();
         Executor executor = mContext.getMainExecutor();
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 0832528..6803431 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -43,6 +43,8 @@
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.internal.util.ArrayUtils;
@@ -66,8 +68,6 @@
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import androidx.test.InstrumentationRegistry;
-
 public class SubscriptionManagerTest {
     private SubscriptionManager mSm;
 
@@ -550,10 +550,11 @@
             }
         };
 
-        int [] subList = mSm.getActiveSubscriptionIdList();
+        List<SubscriptionInfo> subscriptionInfos = mSm.getActiveSubscriptionInfoList();
         boolean changes = false;
 
-        for (int subId : subList) {
+        for (SubscriptionInfo subInfo : subscriptionInfos) {
+            int subId = subInfo.getSubscriptionId();
             if (subId != preferredSubId) {
                 int newPreferredSubId = subId;
                 // Change to a new value.
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
index 05acf32..3d449f2 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
@@ -59,7 +59,36 @@
             String[] selectionArgs = null;
             Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
                     APN_PROJECTION, selection, selectionArgs, null);
-            fail("Expected SecurityExceptio");
+            fail("Expected SecurityException");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    public void testNoAccessToPasswordThruSort() {
+        try {
+            String selection = Carriers.CURRENT + " IS NOT NULL";
+            String[] selectionArgs = null;
+            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
+                    + " password LIKE 'a%') > 0) THEN 1 ELSE 0 END";
+            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
+                    APN_PROJECTION, selection, selectionArgs, sort);
+            fail("Expected SecurityException");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    public void testNoAccessToUser() {
+        try {
+            String selection = Carriers.CURRENT + " IS NOT NULL AND "
+                    + Carriers.USER + " IS NOT NULL";
+            String[] selectionArgs = null;
+            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
+                    + " user LIKE 'a%') > 0) THEN 1 ELSE 0 END";
+            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
+                    APN_PROJECTION, selection, selectionArgs, sort);
+            fail("Expected SecurityException");
         } catch (SecurityException e) {
             // expected
         }
diff --git a/tests/tests/view/res/layout/view_layout.xml b/tests/tests/view/res/layout/view_layout.xml
index 866ac57..7f4264f 100644
--- a/tests/tests/view/res/layout/view_layout.xml
+++ b/tests/tests/view/res/layout/view_layout.xml
@@ -26,8 +26,8 @@
 
     <android.view.cts.MockView
         android:id="@+id/mock_view"
-        android:layout_width="100dp"
-        android:layout_height="75dp"/>
+        android:layout_width="75dp"
+        android:layout_height="100dp"/>
 
     <android.view.cts.MockView
         android:id="@+id/scroll_view"
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index c7297f1..a934986 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -2044,8 +2044,8 @@
         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
 
         float density = view.getContext().getResources().getDisplayMetrics().density;
-        int size1 = (int) (100 * density + 0.5);
-        int size2 = (int) (75 * density + 0.5);
+        int size1 = (int) (75 * density + 0.5);
+        int size2 = (int) (100 * density + 0.5);
 
         assertTrue(view.hasCalledOnMeasure());
         assertEquals(size1, view.getMeasuredWidth());
@@ -2750,8 +2750,8 @@
         Rect rect = new Rect();
 
         float density = view.getContext().getResources().getDisplayMetrics().density;
-        int size1 = (int) (100 * density + 0.5);
-        int size2 = (int) (75 * density + 0.5);
+        int size1 = (int) (75 * density + 0.5);
+        int size2 = (int) (100 * density + 0.5);
 
         assertTrue(view.getLocalVisibleRect(rect));
         assertEquals(0, rect.left);
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 84a7999..9c9217b 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -46,6 +46,7 @@
 import android.view.Display;
 import android.view.PointerIcon;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.test.InstrumentationRegistry;
@@ -104,6 +105,8 @@
         // Set the NULL pointer icon so that it won't obstruct the captured image.
         getWindow().getDecorView().setPointerIcon(
                 PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
 
         mProjectionManager =
                 (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);