Merge "Verify VPN disclosure message via CTS verifier" into rvc-dev
diff --git a/apps/CameraITS/tests/scene1_1/test_linearity.py b/apps/CameraITS/tests/scene1_1/test_linearity.py
index e029ac7..f98f286 100644
--- a/apps/CameraITS/tests/scene1_1/test_linearity.py
+++ b/apps/CameraITS/tests/scene1_1/test_linearity.py
@@ -100,14 +100,16 @@
         pylab.ylabel('RGB avg [0, 1]')
         matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
-        # Check that each plot is actually linear.
+        # Check that each plot is linear with positive slope
         for means in [r_means, g_means, b_means]:
             line, residuals, _, _, _ = numpy.polyfit(range(len(sensitivities)),
                                                      means, 1, full=True)
             print 'Line: m=%f, b=%f, resid=%f'%(line[0], line[1], residuals[0])
-            msg = 'residual: %.5f, THRESH: %.4f' % (
+            e_msg = 'residual: %.5f, THRESH: %.4f' % (
                     residuals[0], RESIDUAL_THRESHOLD)
-            assert residuals[0] < RESIDUAL_THRESHOLD, msg
+            assert residuals[0] < RESIDUAL_THRESHOLD, e_msg
+            e_msg = 'slope %.6f less than 0!' % line[0]
+            assert line[0] > 0, e_msg
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
index c44575f..67216ef 100644
--- a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
+++ b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
@@ -22,10 +22,8 @@
 import its.objects
 import numpy as np
 
-FMT_ATOL = 0.01  # Absolute tolerance on format ratio
-AR_CHECKED = ["4:3", "16:9", "18:9"]  # Aspect ratios checked
 FOV_PERCENT_RTOL = 0.15  # Relative tolerance on circle FoV % to expected
-LARGE_SIZE = 2000   # Define the size of a large image
+LARGE_SIZE = 2000   # Define the size of a large image (compare against max(w,h))
 NAME = os.path.basename(__file__).split(".")[0]
 NUM_DISTORT_PARAMS = 5
 THRESH_L_AR = 0.02  # aspect ratio test threshold of large images
@@ -36,91 +34,123 @@
 PREVIEW_SIZE = (1920, 1080)  # preview size
 
 
-def convert_ar_to_float(ar_string):
-    """Convert aspect ratio string into float.
+def calc_expected_circle_image_ratio(ref_fov, img_w, img_h):
+    """Determine the circle image area ratio in percentage for a given image size.
 
     Args:
-        ar_string:  "4:3" or "16:9"
+        ref_fov:    dict with [fmt, % coverage, w, h, circle_w, circle_h]
+        img_w:      the image width
+        img_h:      the image height
+
     Returns:
-        float(ar_string)
+        chk_percent: the expected circle image area ratio in percentage
     """
-    ar_list = [float(x) for x in ar_string.split(":")]
-    return ar_list[0] / ar_list[1]
+
+    ar_ref = float(ref_fov["w"]) / ref_fov["h"]
+    ar_target = float(img_w) / img_h
+    # The cropping will happen either horizontally or vertically.
+    # In both case a crop results in the visble area reduce by a ratio r (r < 1.0)
+    # and the circle will in turn occupy ref_pct / r (percent) on the target
+    # image size.
+    r = ar_ref / ar_target
+    if r < 1.0:
+        r = 1.0 / r
+    return ref_fov["percent"] * r
 
 
-def determine_sensor_aspect_ratio(props):
-    """Determine the aspect ratio of the sensor.
+def find_raw_fov_reference(cam, req, props, debug):
+    """Determine the circle coverage of the image in RAW reference image.
 
     Args:
+        cam:        camera object
+        req:        camera request
         props:      camera properties
+        debug:      perform debug dump or not
+
     Returns:
-        matched entry in AR_CHECKED
+        ref_fov:         dict with [fmt, % coverage, w, h, circle_w, circle_h]
+        cc_ct_gt:        circle center position relative to the center of image.
+        aspect_ratio_gt: aspect ratio of the detected circle in float.
     """
-    match_ar = None
-    sensor_size = props["android.sensor.info.preCorrectionActiveArraySize"]
-    sensor_ar = (float(abs(sensor_size["right"] - sensor_size["left"])) /
-                 abs(sensor_size["bottom"] - sensor_size["top"]))
-    for ar_string in AR_CHECKED:
-        if np.isclose(sensor_ar, convert_ar_to_float(ar_string), atol=FMT_ATOL):
-            match_ar = ar_string
-    if not match_ar:
-        print "Warning! RAW aspect ratio not in:", AR_CHECKED
-    return match_ar
 
+    # Capture full-frame raw. Use its aspect ratio and circle center
+    # location as ground truth for the other jpeg or yuv images.
+    print "Creating references for fov_coverage from RAW"
+    out_surface = {"format": "raw"}
+    cap_raw = cam.do_capture(req, out_surface)
+    print "Captured %s %dx%d" % ("raw", cap_raw["width"],
+                                 cap_raw["height"])
+    img_raw = its.image.convert_capture_to_rgb_image(cap_raw,
+                                                     props=props)
 
-def aspect_ratio_scale_factors(ref_ar_string, props, raw_avlb):
-    """Determine scale factors for each aspect ratio to correct cropping.
+    # The intrinsics and distortion coefficients are meant for full
+    # size RAW, but convert_capture_to_rgb_image returns a 2x downsampled
+    # version, so resize back to full size here.
+    img_raw = cv2.resize(img_raw, (0, 0), fx=2.0, fy=2.0)
 
-    Args:
-        ref_ar_string:      camera aspect ratio that is the reference
-        props:              camera properties
-        raw_avlb:           bool; RAW available
-    Returns:
-        dict of correction ratios with AR_CHECKED values as keys
-    """
-    ref_ar = convert_ar_to_float(ref_ar_string)
+    # If the device supports lens distortion correction, apply the
+    # coefficients on the RAW image so it can be compared to YUV/JPEG
+    # outputs which are subject to the same correction via ISP.
+    if its.caps.distortion_correction(props):
+        # Intrinsic cal is of format: [f_x, f_y, c_x, c_y, s]
+        # [f_x, f_y] is the horizontal and vertical focal lengths,
+        # [c_x, c_y] is the position of the optical axis,
+        # and s is skew of sensor plane vs lens plane.
+        print "Applying intrinsic calibration and distortion params"
+        ical = np.array(props["android.lens.intrinsicCalibration"])
+        msg = "Cannot include lens distortion without intrinsic cal!"
+        assert len(ical) == 5, msg
+        sensor_h = props["android.sensor.info.physicalSize"]["height"]
+        sensor_w = props["android.sensor.info.physicalSize"]["width"]
+        pixel_h = props["android.sensor.info.pixelArraySize"]["height"]
+        pixel_w = props["android.sensor.info.pixelArraySize"]["width"]
+        fd = float(cap_raw["metadata"]["android.lens.focalLength"])
+        fd_w_pix = pixel_w * fd / sensor_w
+        fd_h_pix = pixel_h * fd / sensor_h
+        # transformation matrix
+        # k = [[f_x, s, c_x],
+        #      [0, f_y, c_y],
+        #      [0,   0,   1]]
+        k = np.array([[ical[0], ical[4], ical[2]],
+                      [0, ical[1], ical[3]],
+                      [0, 0, 1]])
+        print "k:", k
+        e_msg = "fd_w(pixels): %.2f\tcal[0](pixels): %.2f\tTOL=20%%" % (
+                fd_w_pix, ical[0])
+        assert np.isclose(fd_w_pix, ical[0], rtol=0.20), e_msg
+        e_msg = "fd_h(pixels): %.2f\tcal[1](pixels): %.2f\tTOL=20%%" % (
+                fd_h_pix, ical[0])
+        assert np.isclose(fd_h_pix, ical[1], rtol=0.20), e_msg
 
-    # find sensor area that is closest to 4:3 or 16:9
-    h_max = 0
-    w_max = 0
-    for ar_string in AR_CHECKED:
-        match_ar = [float(x) for x in ar_string.split(":")]
-        try:
-            f = its.objects.get_largest_jpeg_format(props, match_ar=match_ar)
-            h = f["height"]
-            w = f["width"]
-            if h > h_max:
-                h_max = h
-            if w > w_max:
-                w_max = w
-        except IndexError:
-            continue
-    if raw_avlb:
-        sensor_size = props["android.sensor.info.preCorrectionActiveArraySize"]
-        w_sensor = abs(sensor_size["right"] - sensor_size["left"])
-        h_sensor = abs(sensor_size["bottom"] - sensor_size["top"])
-        assert w_max <= w_sensor, "w_max: %d, w_sensor: %d" % (w_max, w_sensor)
-        assert h_max <= h_sensor, "h_max: %d, h_sensor: %d" % (h_max, h_sensor)
-    sensor_ar = float(w_max) / h_max
-
-    # apply scaling
-    ar_scaling = {}
-    for ar_string in AR_CHECKED:
-        target_ar = convert_ar_to_float(ar_string)
-        # scale down to sensor with greater (or equal) dims
-        if ref_ar >= sensor_ar:
-            scaling = sensor_ar / ref_ar
-        else:
-            scaling = ref_ar / sensor_ar
-
-        # scale up due to cropping to other format
-        if target_ar >= sensor_ar:
-            scaling = scaling * target_ar / sensor_ar
-        else:
-            scaling = scaling * sensor_ar / target_ar
-
-        ar_scaling[ar_string] = scaling
-    return ar_scaling
+        # distortion
+        rad_dist = props["android.lens.distortion"]
+        print "android.lens.distortion:", rad_dist
+        e_msg = "%s param(s) found. %d expected." % (len(rad_dist),
+                                                     NUM_DISTORT_PARAMS)
+        assert len(rad_dist) == NUM_DISTORT_PARAMS, e_msg
+        opencv_dist = np.array([rad_dist[0], rad_dist[1],
+                                rad_dist[3], rad_dist[4],
+                                rad_dist[2]])
+        print "dist:", opencv_dist
+        img_raw = cv2.undistort(img_raw, k, opencv_dist)
+    size_raw = img_raw.shape
+    w_raw = size_raw[1]
+    h_raw = size_raw[0]
+    img_name = "%s_%s_w%d_h%d.png" % (NAME, "raw", w_raw, h_raw)
+    its.image.write_image(img_raw, img_name, True)
+    aspect_ratio_gt, cc_ct_gt, circle_size_raw = measure_aspect_ratio(
+            img_raw, img_name, True, debug)
+    raw_fov_percent = calc_circle_image_ratio(
+            circle_size_raw[0], circle_size_raw[1], w_raw, h_raw)
+    ref_fov = {}
+    ref_fov["fmt"] = "RAW"
+    ref_fov["percent"] = raw_fov_percent
+    ref_fov["w"] = w_raw
+    ref_fov["h"] = h_raw
+    ref_fov["circle_w"] = circle_size_raw[0]
+    ref_fov["circle_h"] = circle_size_raw[1]
+    print "Using RAW reference:", ref_fov
+    return ref_fov, cc_ct_gt, aspect_ratio_gt
 
 
 def find_jpeg_fov_reference(cam, req, props):
@@ -132,44 +162,33 @@
         props:      camera properties
 
     Returns:
-        ref_fov:    dict with [fmt, % coverage, w, h]
+        ref_fov:    dict with [fmt, % coverage, w, h, circle_w, circle_h]
     """
     ref_fov = {}
-    fmt_dict = {}
-
-    # find number of pixels in different formats
-    for ar in AR_CHECKED:
-        match_ar = [float(x) for x in ar.split(":")]
-        try:
-            f = its.objects.get_largest_jpeg_format(props, match_ar=match_ar)
-            fmt_dict[f["height"]*f["width"]] = {"fmt": f, "ar": ar}
-        except IndexError:
-            continue
-
-    # use image with largest coverage as reference
-    ar_max_pixels = max(fmt_dict, key=int)
-
+    fmt = its.objects.get_largest_jpeg_format(props)
     # capture and determine circle area in image
-    cap = cam.do_capture(req, fmt_dict[ar_max_pixels]["fmt"])
+    cap = cam.do_capture(req, fmt)
     w = cap["width"]
     h = cap["height"]
-    fmt = cap["format"]
 
     img = its.image.convert_capture_to_rgb_image(cap, props=props)
-    print "Captured %s %dx%d" % (fmt, w, h)
-    img_name = "%s_%s_w%d_h%d.png" % (NAME, fmt, w, h)
-    _, _, circle_size = measure_aspect_ratio(img, False, img_name, True)
-    fov_percent = calc_circle_image_ratio(circle_size[1], circle_size[0], w, h)
-    ref_fov["fmt"] = fmt_dict[ar_max_pixels]["ar"]
+    print "Captured JPEG %dx%d" % (w, h)
+    img_name = "%s_jpeg_w%d_h%d.png" % (NAME, w, h)
+    # Set debug to True to save the debug image
+    _, _, circle_size = measure_aspect_ratio(img, img_name, False, debug=True)
+    fov_percent = calc_circle_image_ratio(circle_size[0], circle_size[1], w, h)
+    ref_fov["fmt"] = "JPEG"
     ref_fov["percent"] = fov_percent
     ref_fov["w"] = w
     ref_fov["h"] = h
+    ref_fov["circle_w"] = circle_size[0]
+    ref_fov["circle_h"] = circle_size[1]
     print "Using JPEG reference:", ref_fov
     return ref_fov
 
 
 def calc_circle_image_ratio(circle_w, circle_h, image_w, image_h):
-    """Calculate the circle coverage of the image.
+    """Calculate the percent of area the input circle covers in input image.
 
     Args:
         circle_w (int):      width of circle
@@ -185,329 +204,14 @@
     return fov_percent
 
 
-def main():
-    """Test aspect ratio & check if images are cropped correctly for each fmt.
-
-    Aspect ratio test runs on level3, full and limited devices. Crop test only
-    runs on full and level3 devices.
-    The test image is a black circle inside a black square. When raw capture is
-    available, set the height vs. width ratio of the circle in the full-frame
-    raw as ground truth. Then compare with images of request combinations of
-    different formats ("jpeg" and "yuv") and sizes.
-    If raw capture is unavailable, take a picture of the test image right in
-    front to eliminate shooting angle effect. the height vs. width ratio for
-    the circle should be close to 1. Considering shooting position error, aspect
-    ratio greater than 1+THRESH_*_AR or less than 1-THRESH_*_AR will FAIL.
-    """
-    aspect_ratio_gt = 1  # ground truth
-    failed_ar = []  # streams failed the aspect ration test
-    failed_crop = []  # streams failed the crop test
-    failed_fov = []  # streams that fail FoV test
-    format_list = []  # format list for multiple capture objects.
-    # Do multi-capture of "iter" and "cmpr". Iterate through all the
-    # available sizes of "iter", and only use the size specified for "cmpr"
-    # Do single-capture to cover untouched sizes in multi-capture when needed.
-    format_list.append({"iter": "yuv", "iter_max": None,
-                        "cmpr": "yuv", "cmpr_size": PREVIEW_SIZE})
-    format_list.append({"iter": "yuv", "iter_max": PREVIEW_SIZE,
-                        "cmpr": "jpeg", "cmpr_size": None})
-    format_list.append({"iter": "yuv", "iter_max": PREVIEW_SIZE,
-                        "cmpr": "raw", "cmpr_size": None})
-    format_list.append({"iter": "jpeg", "iter_max": None,
-                        "cmpr": "raw", "cmpr_size": None})
-    format_list.append({"iter": "jpeg", "iter_max": None,
-                        "cmpr": "yuv", "cmpr_size": PREVIEW_SIZE})
-    ref_fov = {}
-    with its.device.ItsSession() as cam:
-        props = cam.get_camera_properties()
-        props = cam.override_with_hidden_physical_camera_props(props)
-        # determine skip conditions
-        first_api = its.device.get_first_api_level(its.device.get_device_id())
-        if first_api < 30:  # original constraint
-            its.caps.skip_unless(its.caps.read_3a(props))
-        else:  # loosen from read_3a to enable LIMITED coverage
-            its.caps.skip_unless(its.caps.ae_lock(props) and
-                                 its.caps.awb_lock(props))
-        # determine capabilities
-        full_device = its.caps.full_or_better(props)
-        level3_device = its.caps.level3(props)
-        raw_avlb = its.caps.raw16(props)
-        run_crop_test = (level3_device or full_device) and raw_avlb
-        if not run_crop_test:
-            print "Crop test skipped"
-        debug = its.caps.debug_mode()
-
-        # Converge 3A
-        cam.do_3a()
-        req = its.objects.auto_capture_request()
-
-        # If raw capture is available, use it as ground truth.
-        if raw_avlb:
-            # Capture full-frame raw. Use its aspect ratio and circle center
-            # location as ground truth for the other jpeg or yuv images.
-            print "Creating references for fov_coverage from RAW"
-            out_surface = {"format": "raw"}
-            cap_raw = cam.do_capture(req, out_surface)
-            print "Captured %s %dx%d" % ("raw", cap_raw["width"],
-                                         cap_raw["height"])
-            img_raw = its.image.convert_capture_to_rgb_image(cap_raw,
-                                                             props=props)
-            if its.caps.distortion_correction(props):
-                # The intrinsics and distortion coefficients are meant for full
-                # size RAW. Resize back to full size here.
-                img_raw = cv2.resize(img_raw, (0, 0), fx=2.0, fy=2.0)
-                # Intrinsic cal is of format: [f_x, f_y, c_x, c_y, s]
-                # [f_x, f_y] is the horizontal and vertical focal lengths,
-                # [c_x, c_y] is the position of the optical axis,
-                # and s is skew of sensor plane vs lens plane.
-                print "Applying intrinsic calibration and distortion params"
-                ical = np.array(props["android.lens.intrinsicCalibration"])
-                msg = "Cannot include lens distortion without intrinsic cal!"
-                assert len(ical) == 5, msg
-                sensor_h = props["android.sensor.info.physicalSize"]["height"]
-                sensor_w = props["android.sensor.info.physicalSize"]["width"]
-                pixel_h = props["android.sensor.info.pixelArraySize"]["height"]
-                pixel_w = props["android.sensor.info.pixelArraySize"]["width"]
-                fd = float(cap_raw["metadata"]["android.lens.focalLength"])
-                fd_w_pix = pixel_w * fd / sensor_w
-                fd_h_pix = pixel_h * fd / sensor_h
-                # transformation matrix
-                # k = [[f_x, s, c_x],
-                #      [0, f_y, c_y],
-                #      [0,   0,   1]]
-                k = np.array([[ical[0], ical[4], ical[2]],
-                              [0, ical[1], ical[3]],
-                              [0, 0, 1]])
-                print "k:", k
-                e_msg = "fd_w(pixels): %.2f\tcal[0](pixels): %.2f\tTOL=20%%" % (
-                        fd_w_pix, ical[0])
-                assert np.isclose(fd_w_pix, ical[0], rtol=0.20), e_msg
-                e_msg = "fd_h(pixels): %.2f\tcal[1](pixels): %.2f\tTOL=20%%" % (
-                        fd_h_pix, ical[0])
-                assert np.isclose(fd_h_pix, ical[1], rtol=0.20), e_msg
-
-                # distortion
-                rad_dist = props["android.lens.distortion"]
-                print "android.lens.distortion:", rad_dist
-                e_msg = "%s param(s) found. %d expected." % (len(rad_dist),
-                                                             NUM_DISTORT_PARAMS)
-                assert len(rad_dist) == NUM_DISTORT_PARAMS, e_msg
-                opencv_dist = np.array([rad_dist[0], rad_dist[1],
-                                        rad_dist[3], rad_dist[4],
-                                        rad_dist[2]])
-                print "dist:", opencv_dist
-                img_raw = cv2.undistort(img_raw, k, opencv_dist)
-            size_raw = img_raw.shape
-            w_raw = size_raw[1]
-            h_raw = size_raw[0]
-            img_name = "%s_%s_w%d_h%d.png" % (NAME, "raw", w_raw, h_raw)
-            its.image.write_image(img_raw, img_name, True)
-            aspect_ratio_gt, cc_ct_gt, circle_size_raw = measure_aspect_ratio(
-                    img_raw, raw_avlb, img_name, debug)
-            raw_fov_percent = calc_circle_image_ratio(
-                    circle_size_raw[1], circle_size_raw[0], w_raw, h_raw)
-            # Normalize the circle size to 1/4 of the image size, so that
-            # circle size won't affect the crop test result
-            factor_cp_thres = (min(size_raw[0:1])/4.0) / max(circle_size_raw)
-            thres_l_cp_test = THRESH_L_CP * factor_cp_thres
-            thres_xs_cp_test = THRESH_XS_CP * factor_cp_thres
-            # If RAW in AR_CHECKED, use it as reference
-            ref_fov["fmt"] = determine_sensor_aspect_ratio(props)
-            if ref_fov["fmt"]:
-                ref_fov["percent"] = raw_fov_percent
-                ref_fov["w"] = w_raw
-                ref_fov["h"] = h_raw
-                print "Using RAW reference:", ref_fov
-            else:
-                ref_fov = find_jpeg_fov_reference(cam, req, props)
-        else:
-            ref_fov = find_jpeg_fov_reference(cam, req, props)
-
-        # Determine scaling factors for AR calculations
-        ar_scaling = aspect_ratio_scale_factors(ref_fov["fmt"], props, raw_avlb)
-
-        # Take pictures of each settings with all the image sizes available.
-        for fmt in format_list:
-            fmt_iter = fmt["iter"]
-            fmt_cmpr = fmt["cmpr"]
-            dual_target = fmt_cmpr is not "none"
-            # Get the size of "cmpr"
-            if dual_target:
-                sizes = its.objects.get_available_output_sizes(
-                        fmt_cmpr, props, fmt["cmpr_size"])
-                if not sizes:  # device might not support RAW
-                    continue
-                size_cmpr = sizes[0]
-            for size_iter in its.objects.get_available_output_sizes(
-                    fmt_iter, props, fmt["iter_max"]):
-                w_iter = size_iter[0]
-                h_iter = size_iter[1]
-                # Skip testing same format/size combination
-                # ITS does not handle that properly now
-                if (dual_target
-                            and w_iter*h_iter == size_cmpr[0]*size_cmpr[1]
-                            and fmt_iter == fmt_cmpr):
-                    continue
-                out_surface = [{"width": w_iter,
-                                "height": h_iter,
-                                "format": fmt_iter}]
-                if dual_target:
-                    out_surface.append({"width": size_cmpr[0],
-                                        "height": size_cmpr[1],
-                                        "format": fmt_cmpr})
-                cap = cam.do_capture(req, out_surface)
-                if dual_target:
-                    frm_iter = cap[0]
-                else:
-                    frm_iter = cap
-                assert frm_iter["format"] == fmt_iter
-                assert frm_iter["width"] == w_iter
-                assert frm_iter["height"] == h_iter
-                print "Captured %s with %s %dx%d. Compared size: %dx%d" % (
-                        fmt_iter, fmt_cmpr, w_iter, h_iter, size_cmpr[0],
-                        size_cmpr[1])
-                img = its.image.convert_capture_to_rgb_image(frm_iter)
-                if its.caps.distortion_correction(props) and raw_avlb:
-                    w_scale = float(w_iter)/w_raw
-                    h_scale = float(h_iter)/h_raw
-                    k_scale = np.array([[ical[0]*w_scale, ical[4],
-                                         ical[2]*w_scale],
-                                        [0, ical[1]*h_scale, ical[3]*h_scale],
-                                        [0, 0, 1]])
-                    print "k_scale:", k_scale
-                    img = cv2.undistort(img, k_scale, opencv_dist)
-                img_name = "%s_%s_with_%s_w%d_h%d.png" % (NAME,
-                                                          fmt_iter, fmt_cmpr,
-                                                          w_iter, h_iter)
-                aspect_ratio, cc_ct, (cc_w, cc_h) = measure_aspect_ratio(
-                        img, raw_avlb, img_name, debug)
-                # check fov coverage for all fmts in AR_CHECKED
-                fov_percent = calc_circle_image_ratio(
-                        cc_w, cc_h, w_iter, h_iter)
-                for ar_check in AR_CHECKED:
-                    match_ar_list = [float(x) for x in ar_check.split(":")]
-                    match_ar = match_ar_list[0] / match_ar_list[1]
-                    if np.isclose(float(w_iter)/h_iter, match_ar,
-                                  atol=FMT_ATOL):
-                        # scale check value based on aspect ratio
-                        chk_percent = ref_fov["percent"] * ar_scaling[ar_check]
-                        if not np.isclose(fov_percent, chk_percent,
-                                          rtol=FOV_PERCENT_RTOL):
-                            msg = "FoV %%: %.2f, Ref FoV %%: %.2f, " % (
-                                    fov_percent, chk_percent)
-                            msg += "TOL=%.f%%, img: %dx%d, ref: %dx%d" % (
-                                    FOV_PERCENT_RTOL*100, w_iter, h_iter,
-                                    ref_fov["w"], ref_fov["h"])
-                            failed_fov.append(msg)
-                            its.image.write_image(img/255, img_name, True)
-                # check pass/fail for aspect ratio
-                # image size >= LARGE_SIZE: use THRESH_L_AR
-                # image size == 0 (extreme case): THRESH_XS_AR
-                # 0 < image size < LARGE_SIZE: scale between THRESH_XS_AR
-                # and THRESH_L_AR
-                thres_ar_test = max(
-                        THRESH_L_AR, THRESH_XS_AR + max(w_iter, h_iter) *
-                        (THRESH_L_AR-THRESH_XS_AR)/LARGE_SIZE)
-                thres_range_ar = (aspect_ratio_gt-thres_ar_test,
-                                  aspect_ratio_gt+thres_ar_test)
-                if (aspect_ratio < thres_range_ar[0] or
-                            aspect_ratio > thres_range_ar[1]):
-                    failed_ar.append({"fmt_iter": fmt_iter,
-                                      "fmt_cmpr": fmt_cmpr,
-                                      "w": w_iter, "h": h_iter,
-                                      "ar": aspect_ratio,
-                                      "valid_range": thres_range_ar})
-                    its.image.write_image(img/255, img_name, True)
-
-                # check pass/fail for crop
-                if run_crop_test:
-                    # image size >= LARGE_SIZE: use thres_l_cp_test
-                    # image size == 0 (extreme case): thres_xs_cp_test
-                    # 0 < image size < LARGE_SIZE: scale between
-                    # thres_xs_cp_test and thres_l_cp_test
-                    # Also, allow at least THRESH_MIN_PIXEL off to
-                    # prevent threshold being too tight for very
-                    # small circle
-                    thres_hori_cp_test = max(
-                            thres_l_cp_test, thres_xs_cp_test + w_iter *
-                            (thres_l_cp_test-thres_xs_cp_test)/LARGE_SIZE)
-                    min_threshold_h = THRESH_MIN_PIXEL / cc_w
-                    thres_hori_cp_test = max(thres_hori_cp_test,
-                                             min_threshold_h)
-                    thres_range_h_cp = (cc_ct_gt["hori"]-thres_hori_cp_test,
-                                        cc_ct_gt["hori"]+thres_hori_cp_test)
-                    thres_vert_cp_test = max(
-                            thres_l_cp_test, thres_xs_cp_test + h_iter *
-                            (thres_l_cp_test-thres_xs_cp_test)/LARGE_SIZE)
-                    min_threshold_v = THRESH_MIN_PIXEL / cc_h
-                    thres_vert_cp_test = max(thres_vert_cp_test,
-                                             min_threshold_v)
-                    thres_range_v_cp = (cc_ct_gt["vert"]-thres_vert_cp_test,
-                                        cc_ct_gt["vert"]+thres_vert_cp_test)
-                    if (cc_ct["hori"] < thres_range_h_cp[0]
-                                or cc_ct["hori"] > thres_range_h_cp[1]
-                                or cc_ct["vert"] < thres_range_v_cp[0]
-                                or cc_ct["vert"] > thres_range_v_cp[1]):
-                        failed_crop.append({"fmt_iter": fmt_iter,
-                                            "fmt_cmpr": fmt_cmpr,
-                                            "w": w_iter, "h": h_iter,
-                                            "ct_hori": cc_ct["hori"],
-                                            "ct_vert": cc_ct["vert"],
-                                            "valid_range_h": thres_range_h_cp,
-                                            "valid_range_v": thres_range_v_cp})
-                        its.image.write_image(img/255, img_name, True)
-
-        # Print aspect ratio test results
-        failed_image_number_for_aspect_ratio_test = len(failed_ar)
-        if failed_image_number_for_aspect_ratio_test > 0:
-            print "\nAspect ratio test summary"
-            print "Images failed in the aspect ratio test:"
-            print "Aspect ratio value: width / height"
-            for fa in failed_ar:
-                print "%s with %s %dx%d: %.3f;" % (
-                        fa["fmt_iter"], fa["fmt_cmpr"],
-                        fa["w"], fa["h"], fa["ar"]),
-                print "valid range: %.3f ~ %.3f" % (
-                        fa["valid_range"][0], fa["valid_range"][1])
-
-        # Print FoV test results
-        failed_image_number_for_fov_test = len(failed_fov)
-        if failed_image_number_for_fov_test > 0:
-            print "\nFoV test summary"
-            print "Images failed in the FoV test:"
-            for fov in failed_fov:
-                print fov
-
-        # Print crop test results
-        failed_image_number_for_crop_test = len(failed_crop)
-        if failed_image_number_for_crop_test > 0:
-            print "\nCrop test summary"
-            print "Images failed in the crop test:"
-            print "Circle center position, (horizontal x vertical), listed",
-            print "below is relative to the image center."
-            for fc in failed_crop:
-                print "%s with %s %dx%d: %.3f x %.3f;" % (
-                        fc["fmt_iter"], fc["fmt_cmpr"], fc["w"], fc["h"],
-                        fc["ct_hori"], fc["ct_vert"]),
-                print "valid horizontal range: %.3f ~ %.3f;" % (
-                        fc["valid_range_h"][0], fc["valid_range_h"][1]),
-                print "valid vertical range: %.3f ~ %.3f" % (
-                        fc["valid_range_v"][0], fc["valid_range_v"][1])
-
-        assert failed_image_number_for_aspect_ratio_test == 0
-        assert failed_image_number_for_fov_test == 0
-        if level3_device:
-            assert failed_image_number_for_crop_test == 0
-
-
-def measure_aspect_ratio(img, raw_avlb, img_name, debug):
+def measure_aspect_ratio(img, img_name, raw_avlb, debug):
     """Measure the aspect ratio of the black circle in the test image.
 
     Args:
         img: Numpy float image array in RGB, with pixel values in [0,1].
+        img_name: string with image info of format and size.
         raw_avlb: True: raw capture is available; False: raw capture is not
              available.
-        img_name: string with image info of format and size.
         debug: boolean for whether in debug mode.
     Returns:
         aspect_ratio: aspect ratio number in float.
@@ -531,6 +235,9 @@
     elif cv2_version.startswith("3.2."):
         _, contours, hierarchy = cv2.findContours(255-img_bw, cv2.RETR_TREE,
                                                   cv2.CHAIN_APPROX_SIMPLE)
+    else:  # OpenCV 4.2
+        contours, hierarchy = cv2.findContours(255-img_bw, cv2.RETR_TREE,
+                                               cv2.CHAIN_APPROX_SIMPLE)
 
     # Check each component and find the black circle
     min_cmpt = size[0] * size[1] * 0.005
@@ -650,5 +357,295 @@
     return aspect_ratio, cc_ct, (circle_w, circle_h)
 
 
+def main():
+    """Test aspect ratio/field of view (FOV)/cropping for each tested formats combinations.
+
+    This test checks for:
+      1. Aspect ratio: images are not stretched
+      2. Crop: center of images is always center of the image sensor no matter
+         how the image is cropped from sensor's full FOV
+      3. FOV: images are always cropped to keep the maximum possible FOV with
+         only one dimension (horizontal or veritical) cropped.
+
+    Aspect ratio and FOV test runs on level3, full and limited devices.
+    Crop test only runs on full and level3 devices.
+
+    The test chart is a black circle inside a black square. When raw capture is
+    available, set the height vs. width ratio of the circle in the full-frame
+    raw as ground truth. In an ideal setup such ratio should be very close to
+    1.0, but here we just use the value derived from full resolution RAW as
+    ground truth to account for the possiblity that the chart is not well
+    positioned to be precisely parallel to image sensor plane.
+    The test then compare the ground truth ratio with the same ratio measured
+    on images captued using different stream combinations of varying formats
+    ("jpeg" and "yuv") and resolutions.
+    If raw capture is unavailable, a full resolution JPEG image is used to setup
+    ground truth. In this case, the ground truth aspect ratio is defined as 1.0
+    and it is the tester's responsibility to make sure the test chart is
+    properly positioned so the detected circles indeed have aspect ratio close
+    to 1.0 assuming no bugs causing image stretched.
+
+    The aspect ratio test checks the aspect ratio of the detected circle and
+    it will fail if the aspect ratio differs too much from the ground truth
+    aspect ratio mentioned above.
+
+    The FOV test examines the ratio between the detected circle area and the
+    image size. When the aspect ratio of the test image is the same as the
+    ground truth image, the ratio should be very close to the ground truth
+    value. When the aspect ratio is different, the difference is factored in
+    per the expectation of the Camera2 API specification, which mandates the
+    FOV reduction from full sensor area must only occur in one dimension:
+    horizontally or vertically, and never both. For example, let's say a sensor
+    has a 16:10 full sensor FOV. For all 16:10 output images there should be no
+    FOV reduction on them. For 16:9 output images the FOV should be vertically
+    cropped by 9/10. For 4:3 output images the FOV should be cropped
+    horizontally instead and the ratio (r) can be calculated as follows:
+        (16 * r) / 10 = 4 / 3 => r = 40 / 48 = 0.8333
+    Say the circle is covering x percent of the 16:10 sensor on the full 16:10
+    FOV, and assume the circle in the center will never be cut in any output
+    sizes (this can be achieved by picking the right size and position of the
+    test circle), the from above cropping expectation we can derive on a 16:9
+    output image the circle will cover (x / 0.9) percent of the 16:9 image; on
+    a 4:3 output image the circle will cover (x / 0.8333) percent of the 4:3
+    image.
+
+    The crop test checks that the center of any output image remains aligned
+    with center of sensor's active area, no matter what kind of cropping or
+    scaling is applied. The test verified that by checking the relative vector
+    from the image center to the center of detected circle remains unchanged.
+    The relative part is normalized by the detected circle size to account for
+    scaling effect.
+    """
+    aspect_ratio_gt = 1.0  # Ground truth circle width/height ratio.
+                           # If full resolution RAW is available as reference
+                           # then this will be updated to the value measured on
+                           # the RAW image. Otherwise a full resolution JPEG
+                           # will be used as reference and this value will be
+                           # 1.0.
+    failed_ar = []  # streams failed the aspect ratio test
+    failed_crop = []  # streams failed the crop test
+    failed_fov = []  # streams that fail FoV test
+    format_list = []  # format list for multiple capture objects.
+
+    # Do multi-capture of "iter" and "cmpr". Iterate through all the
+    # available sizes of "iter", and only use the size specified for "cmpr"
+    # The "cmpr" capture is only used so that we have multiple capture target
+    # instead of just one, which should help catching more potential issues.
+    # The test doesn't look into the output of "cmpr" images at all.
+    # The "iter_max" or "cmpr_size" key defines the maximal size being iterated
+    # or selected for the "iter" and "cmpr" stream accordingly. None means no
+    # upper bound is specified.
+    format_list.append({"iter": "yuv", "iter_max": None,
+                        "cmpr": "yuv", "cmpr_size": PREVIEW_SIZE})
+    format_list.append({"iter": "yuv", "iter_max": PREVIEW_SIZE,
+                        "cmpr": "jpeg", "cmpr_size": None})
+    format_list.append({"iter": "yuv", "iter_max": PREVIEW_SIZE,
+                        "cmpr": "raw", "cmpr_size": None})
+    format_list.append({"iter": "jpeg", "iter_max": None,
+                        "cmpr": "raw", "cmpr_size": None})
+    format_list.append({"iter": "jpeg", "iter_max": None,
+                        "cmpr": "yuv", "cmpr_size": PREVIEW_SIZE})
+    ref_fov = {}  # Reference frame's FOV related information
+                  # If RAW is available a full resolution RAW frame will be used
+                  # as reference frame; otherwise the highest resolution JPEG is used.
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        props = cam.override_with_hidden_physical_camera_props(props)
+        # determine skip conditions
+        first_api = its.device.get_first_api_level(its.device.get_device_id())
+        if first_api < 30:  # original constraint
+            its.caps.skip_unless(its.caps.read_3a(props))
+        else:  # loosen from read_3a to enable LIMITED coverage
+            its.caps.skip_unless(its.caps.ae_lock(props) and
+                                 its.caps.awb_lock(props))
+        # determine capabilities
+        full_device = its.caps.full_or_better(props)
+        limited_device = its.caps.limited(props)
+        its.caps.skip_unless(full_device or limited_device)
+        level3_device = its.caps.level3(props)
+        raw_avlb = its.caps.raw16(props)
+        run_crop_test = (level3_device or full_device) and raw_avlb
+        if not run_crop_test:
+            print "Crop test skipped"
+        debug = its.caps.debug_mode()
+        # Converge 3A
+        cam.do_3a()
+        req = its.objects.auto_capture_request()
+
+        # If raw capture is available, use it as ground truth.
+        if raw_avlb:
+            ref_fov, cc_ct_gt, aspect_ratio_gt = find_raw_fov_reference(cam, req, props, debug)
+        else:
+            ref_fov = find_jpeg_fov_reference(cam, req, props)
+
+        if run_crop_test:
+            # Normalize the circle size to 1/4 of the image size, so that
+            # circle size won't affect the crop test result
+            factor_cp_thres = ((min(ref_fov["w"], ref_fov["h"])/4.0) /
+                               max(ref_fov["circle_w"], ref_fov["circle_h"]))
+            thres_l_cp_test = THRESH_L_CP * factor_cp_thres
+            thres_xs_cp_test = THRESH_XS_CP * factor_cp_thres
+
+        # Take pictures of each settings with all the image sizes available.
+        for fmt in format_list:
+            fmt_iter = fmt["iter"]
+            fmt_cmpr = fmt["cmpr"]
+            dual_target = fmt_cmpr is not "none"
+            # Get the size of "cmpr"
+            if dual_target:
+                sizes = its.objects.get_available_output_sizes(
+                        fmt_cmpr, props, fmt["cmpr_size"])
+                if not sizes:  # device might not support RAW
+                    continue
+                size_cmpr = sizes[0]
+            for size_iter in its.objects.get_available_output_sizes(
+                    fmt_iter, props, fmt["iter_max"]):
+                w_iter = size_iter[0]
+                h_iter = size_iter[1]
+                # Skip testing same format/size combination
+                # ITS does not handle that properly now
+                if (dual_target
+                            and w_iter*h_iter == size_cmpr[0]*size_cmpr[1]
+                            and fmt_iter == fmt_cmpr):
+                    continue
+                out_surface = [{"width": w_iter,
+                                "height": h_iter,
+                                "format": fmt_iter}]
+                if dual_target:
+                    out_surface.append({"width": size_cmpr[0],
+                                        "height": size_cmpr[1],
+                                        "format": fmt_cmpr})
+                cap = cam.do_capture(req, out_surface)
+                if dual_target:
+                    frm_iter = cap[0]
+                else:
+                    frm_iter = cap
+                assert frm_iter["format"] == fmt_iter
+                assert frm_iter["width"] == w_iter
+                assert frm_iter["height"] == h_iter
+                print "Captured %s with %s %dx%d. Compared size: %dx%d" % (
+                        fmt_iter, fmt_cmpr, w_iter, h_iter, size_cmpr[0],
+                        size_cmpr[1])
+                img = its.image.convert_capture_to_rgb_image(frm_iter)
+                img_name = "%s_%s_with_%s_w%d_h%d.png" % (NAME,
+                                                          fmt_iter, fmt_cmpr,
+                                                          w_iter, h_iter)
+                aspect_ratio, cc_ct, (cc_w, cc_h) = measure_aspect_ratio(
+                        img, img_name, raw_avlb, debug)
+                # check fov coverage for all fmts in AR_CHECKED
+                fov_percent = calc_circle_image_ratio(
+                        cc_w, cc_h, w_iter, h_iter)
+                chk_percent = calc_expected_circle_image_ratio(ref_fov, w_iter, h_iter)
+                if not np.isclose(fov_percent, chk_percent,
+                                  rtol=FOV_PERCENT_RTOL):
+                    msg = "FoV %%: %.2f, Ref FoV %%: %.2f, " % (
+                            fov_percent, chk_percent)
+                    msg += "TOL=%.f%%, img: %dx%d, ref: %dx%d" % (
+                            FOV_PERCENT_RTOL*100, w_iter, h_iter,
+                            ref_fov["w"], ref_fov["h"])
+                    failed_fov.append(msg)
+                    its.image.write_image(img/255, img_name, True)
+
+                # check pass/fail for aspect ratio
+                # image size: the larger one of image width and height
+                # image size >= LARGE_SIZE: use THRESH_L_AR
+                # image size == 0 (extreme case): THRESH_XS_AR
+                # 0 < image size < LARGE_SIZE: scale between THRESH_XS_AR
+                # and THRESH_L_AR
+                thres_ar_test = max(
+                        THRESH_L_AR, THRESH_XS_AR + max(w_iter, h_iter) *
+                        (THRESH_L_AR-THRESH_XS_AR)/LARGE_SIZE)
+                thres_range_ar = (aspect_ratio_gt-thres_ar_test,
+                                  aspect_ratio_gt+thres_ar_test)
+                if (aspect_ratio < thres_range_ar[0] or
+                            aspect_ratio > thres_range_ar[1]):
+                    failed_ar.append({"fmt_iter": fmt_iter,
+                                      "fmt_cmpr": fmt_cmpr,
+                                      "w": w_iter, "h": h_iter,
+                                      "ar": aspect_ratio,
+                                      "valid_range": thres_range_ar})
+                    its.image.write_image(img/255, img_name, True)
+
+                # check pass/fail for crop
+                if run_crop_test:
+                    # image size >= LARGE_SIZE: use thres_l_cp_test
+                    # image size == 0 (extreme case): thres_xs_cp_test
+                    # 0 < image size < LARGE_SIZE: scale between
+                    # thres_xs_cp_test and thres_l_cp_test
+                    # Also, allow at least THRESH_MIN_PIXEL off to
+                    # prevent threshold being too tight for very
+                    # small circle
+                    thres_hori_cp_test = max(
+                            thres_l_cp_test, thres_xs_cp_test + w_iter *
+                            (thres_l_cp_test-thres_xs_cp_test)/LARGE_SIZE)
+                    min_threshold_h = THRESH_MIN_PIXEL / cc_w
+                    thres_hori_cp_test = max(thres_hori_cp_test,
+                                             min_threshold_h)
+                    thres_range_h_cp = (cc_ct_gt["hori"]-thres_hori_cp_test,
+                                        cc_ct_gt["hori"]+thres_hori_cp_test)
+                    thres_vert_cp_test = max(
+                            thres_l_cp_test, thres_xs_cp_test + h_iter *
+                            (thres_l_cp_test-thres_xs_cp_test)/LARGE_SIZE)
+                    min_threshold_v = THRESH_MIN_PIXEL / cc_h
+                    thres_vert_cp_test = max(thres_vert_cp_test,
+                                             min_threshold_v)
+                    thres_range_v_cp = (cc_ct_gt["vert"]-thres_vert_cp_test,
+                                        cc_ct_gt["vert"]+thres_vert_cp_test)
+                    if (cc_ct["hori"] < thres_range_h_cp[0]
+                                or cc_ct["hori"] > thres_range_h_cp[1]
+                                or cc_ct["vert"] < thres_range_v_cp[0]
+                                or cc_ct["vert"] > thres_range_v_cp[1]):
+                        failed_crop.append({"fmt_iter": fmt_iter,
+                                            "fmt_cmpr": fmt_cmpr,
+                                            "w": w_iter, "h": h_iter,
+                                            "ct_hori": cc_ct["hori"],
+                                            "ct_vert": cc_ct["vert"],
+                                            "valid_range_h": thres_range_h_cp,
+                                            "valid_range_v": thres_range_v_cp})
+                        its.image.write_image(img/255, img_name, True)
+
+        # Print aspect ratio test results
+        failed_image_number_for_aspect_ratio_test = len(failed_ar)
+        if failed_image_number_for_aspect_ratio_test > 0:
+            print "\nAspect ratio test summary"
+            print "Images failed in the aspect ratio test:"
+            print "Aspect ratio value: width / height"
+            for fa in failed_ar:
+                print "%s with %s %dx%d: %.3f;" % (
+                        fa["fmt_iter"], fa["fmt_cmpr"],
+                        fa["w"], fa["h"], fa["ar"]),
+                print "valid range: %.3f ~ %.3f" % (
+                        fa["valid_range"][0], fa["valid_range"][1])
+
+        # Print FoV test results
+        failed_image_number_for_fov_test = len(failed_fov)
+        if failed_image_number_for_fov_test > 0:
+            print "\nFoV test summary"
+            print "Images failed in the FoV test:"
+            for fov in failed_fov:
+                print fov
+
+        # Print crop test results
+        failed_image_number_for_crop_test = len(failed_crop)
+        if failed_image_number_for_crop_test > 0:
+            print "\nCrop test summary"
+            print "Images failed in the crop test:"
+            print "Circle center position, (horizontal x vertical), listed",
+            print "below is relative to the image center."
+            for fc in failed_crop:
+                print "%s with %s %dx%d: %.3f x %.3f;" % (
+                        fc["fmt_iter"], fc["fmt_cmpr"], fc["w"], fc["h"],
+                        fc["ct_hori"], fc["ct_vert"]),
+                print "valid horizontal range: %.3f ~ %.3f;" % (
+                        fc["valid_range_h"][0], fc["valid_range_h"][1]),
+                print "valid vertical range: %.3f ~ %.3f" % (
+                        fc["valid_range_v"][0], fc["valid_range_v"][1])
+
+        assert failed_image_number_for_aspect_ratio_test == 0
+        assert failed_image_number_for_fov_test == 0
+        if level3_device:
+            assert failed_image_number_for_crop_test == 0
+
+
 if __name__ == "__main__":
     main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index f104bf3..f901d24 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -40,6 +40,7 @@
     <uses-permission android:name="android.permission.FULLSCREEN" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.NFC" />
+    <uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.REQUEST_PASSWORD_COMPLEXITY" />
@@ -1733,6 +1734,16 @@
                 android:configChanges="keyboardHidden|orientation|screenSize">
         </activity>
 
+        <activity android:name="com.android.cts.verifier.nfc.offhost.OffhostUiccReaderTestActivity"
+                android:label="@string/nfc_offhost_uicc_reader_tests"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
+        <activity android:name="com.android.cts.verifier.nfc.offhost.OffhostUiccEmulatorTestActivity"
+                android:label="@string/nfc_offhost_uicc_emulator_tests"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
         <activity android:name=".nfc.NdefPushSenderActivity"
                 android:label="@string/nfc_ndef_push_sender"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -1845,6 +1856,22 @@
                 android:label="@string/nfc_hce_f_reader"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <activity android:name=".nfc.offhost.UiccTransactionEvent1EmulatorActivity"
+                android:label="@string/nfc_offhost_uicc_transaction_event1_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.offhost.UiccTransactionEvent2EmulatorActivity"
+                android:label="@string/nfc_offhost_uicc_transaction_event2_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.offhost.UiccTransactionEvent3EmulatorActivity"
+                android:label="@string/nfc_offhost_uicc_transaction_event3_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.offhost.SimpleOffhostReaderActivity"
+                android:label="@string/nfc_offhost_uicc_transaction_event1_reader"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <!-- services used for testing NFC host-based card emulation -->
         <service android:name=".nfc.hce.PaymentService1" android:exported="true"
                  android:permission="android.permission.BIND_NFC_SERVICE"
@@ -1983,6 +2010,34 @@
             </intent-filter>
             <meta-data android:name="android.nfc.cardemulation.host_nfcf_service" android:resource="@xml/felicaservice"/>
         </service>
+
+        <service
+            android:name=".nfc.offhost.UiccTransactionEventService"
+            android:enabled="true"
+            android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE">
+          <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/uicc_transaction_event_aid_list"/>
+        </service>
+
+        <receiver android:name=".nfc.offhost.UiccTransactionEventReceiver">
+            <intent-filter>
+                <action android:name="android.nfc.action.TRANSACTION_DETECTED" >
+                </action>
+
+                <category android:name="android.intent.category.DEFAULT" >
+                </category>
+
+                <data
+                    android:host="secure"
+                    android:pathPattern="/SIM.*/A000000476416E64726F696443545341"
+                    android:port="0"
+                    android:scheme="nfc" />
+            </intent-filter>
+        </receiver>
+
         <!-- Service used for Camera ITS tests -->
         <service android:name=".camera.its.ItsService"
             android:foregroundServiceType="camera">
@@ -2446,6 +2501,18 @@
                        android:resource="@xml/shortcuts" />
         </activity>
 
+        <activity android:name=".notifications.MediaPlayerVerifierActivity"
+                  android:label="@string/qs_media_player_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category"
+                       android:value="@string/test_category_notifications" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" />
+        </activity>
+
         <service android:name=".notifications.MockListener"
           android:exported="true"
           android:label="@string/nls_service_name"
@@ -3696,7 +3763,19 @@
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+        </activity>
+
+        <activity android:name=".audio.USBAudioPeripheralNotificationsTest"
+                  android:label="@string/audio_uap_notifications_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_audio" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".audio.USBAudioPeripheralPlayActivity"
@@ -3708,7 +3787,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".audio.USBAudioPeripheralRecordActivity"
@@ -3720,7 +3799,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".audio.USBAudioPeripheralButtonsActivity"
@@ -3732,7 +3811,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".audio.USBRestrictRecordAActivity"
@@ -3744,7 +3823,7 @@
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".audio.ProAudioActivity"
diff --git a/apps/CtsVerifier/res/layout/uap_notifications_layout.xml b/apps/CtsVerifier/res/layout/uap_notifications_layout.xml
new file mode 100644
index 0000000..060c664
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/uap_notifications_layout.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2020 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"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <!-- Device Notifications -->
+    <!-- USB_HEADSET -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="TYPE_USB_HEADSET" />
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:id="@+id/uap_messages_headset_out_name"/>
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:id="@+id/uap_messages_headset_in_name"/>
+    </LinearLayout>
+
+    <!-- USB_DEVICE -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="TYPE_USB_DEVICE"/>
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:id="@+id/uap_messages_usb_device__out_name"/>
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:id="@+id/uap_messages_usb_device_in_name"/>
+    </LinearLayout>
+
+    <!-- Plug Intent -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="ACTION_HEADSET_PLUG Intent"/>
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:id="@+id/uap_messages_plug_message"/>
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons"/>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/wifi_main.xml b/apps/CtsVerifier/res/layout/wifi_main.xml
index fee710d..67ecb9a 100644
--- a/apps/CtsVerifier/res/layout/wifi_main.xml
+++ b/apps/CtsVerifier/res/layout/wifi_main.xml
@@ -13,39 +13,69 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                style="@style/RootLayoutPadding">
+
+        <TextView android:id="@+id/wifi_ssid_label"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/wifi_ssid_label"
+              style="@style/InstructionsSmallFont"
+        />
+        <EditText android:id="@+id/wifi_ssid"
               android:layout_width="match_parent"
-              android:layout_height="match_parent"
->
-
-    <FrameLayout
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1.0"
-    >
-
-        <ScrollView android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-        >
-            <TextView android:id="@+id/wifi_info"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content"
-                      style="@style/InstructionsFont"
-            />
-        </ScrollView>
-
-        <ProgressBar
-            android:id="@+id/wifi_progress"
+              android:layout_height="wrap_content"
+              android:layout_below="@id/wifi_ssid_label"
+              android:inputType="text"
+              style="@style/InstructionsSmallFont"
+        />
+        <TextView android:id="@+id/wifi_psk_label"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_below="@id/wifi_ssid"
+              android:text="@string/wifi_psk_label"
+              style="@style/InstructionsSmallFont"
+        />
+        <EditText android:id="@+id/wifi_psk"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_below="@id/wifi_psk_label"
+              android:inputType="textPassword"
+              style="@style/InstructionsSmallFont"
+        />
+        <Button android:id="@+id/wifi_start_test_btn"
+            android:text="@string/wifi_start_test_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_psk"
+            android:layout_centerHorizontal="true"
+        />
+
+        <ScrollView android:id="@+id/wifi_info_scroll_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_below="@id/wifi_start_test_btn"
+        >
+        <TextView android:id="@+id/wifi_info"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/InstructionsSmallFont"
+        />
+        </ScrollView>
+        <ProgressBar android:id="@+id/wifi_progress"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_info_scroll_view"
             android:indeterminate="true"
             android:layout_gravity="center"
             android:visibility="gone"
         />
-    </FrameLayout>
-
-    <include layout="@layout/pass_fail_buttons" />
-
-</LinearLayout>
+        <include android:id="@+id/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"/>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 81566be..f246e0c 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1169,6 +1169,39 @@
     <string name="accessService">AccessService</string>
     <string name="offhostService">OffhostService</string>
     <string name="felicaservice">Felica Service</string>
+    <string name="UiccTransactionEventService">Uicc Transaction Event Service</string>
+
+    <string name="nfc_offhost_uicc">Offhost UICC-based card emulation</string>
+    <string name="nfc_offhost_uicc_emulator_tests">Offhost card emulation emulator tests</string>
+    <string name="nfc_offhost_uicc_reader_tests">Offhost card emulation reader tests</string>
+    <string name="nfc_offhost_uicc_transaction_event1_emulator">Offhost transaction event-field off (Emulator)</string>
+    <string name="nfc_offhost_uicc_transaction_event1_reader">Offhost transaction event-field off (Reader)</string>
+    <string name="nfc_offhost_uicc_transaction_event2_emulator">Offhost transaction event-deselect (Emulator)</string>
+    <string name="nfc_offhost_uicc_transaction_event2_reader">Offhost transaction event-deselect (Reader)</string>
+    <string name="nfc_offhost_uicc_transaction_event3_emulator">Offhost transaction event-HCI CMD (Emulator)</string>
+    <string name="nfc_offhost_uicc_transaction_event3_reader">Offhost transaction event-HCI CMD (Reader)</string>
+
+    <string name="nfc_offhost_uicc_emulator_test_info">The offhost-based card emulation
+        tests require two devices to be completed. The emulator tests are used
+        to actually test the off-based card emulation feature of the device-under-test. So the
+        device running the emulator tests must be the candidate device running the software
+        to be tested. \n\nFor each emulator test, there is a corresponding "reader test"
+        in the "Offhost reader tests" section. The "reader test" acts as a NFC terminal/POS
+        and makes sure the device-under-test implements card emulation correctly
+    </string>
+    <string name="nfc_offhost_uicc_reader_test_info">The offhost-based card emulation
+        tests require two devices to be completed. The emulator tests are used
+        to actually test the off-based card emulation feature of the device-under-test. So the
+        device running the emulator tests must be the candidate device running the software
+        to be tested. \n\nFor each emulator test, there is a corresponding "reader test"
+        in the "Offhost card emulation reader tests" section. The "reader test" acts as a NFC terminal/POS
+        and makes sure the device-under-test implements card emulation correctly.
+    </string>
+    <string name="nfc_offhost_uicc_type_selection">By default Offhost card emulation applications run on type A.
+      If your device supports type B (for example, because of a type B only UICC), select "Type B" from the drop-down box above. Note that all tests must be completed in the same mode (either "Type A" or "Type B").</string>
+    <string name="nfc_offhost_please_wait">Please wait</string>
+    <string name="nfc_offhost_setting_up">Setting up card emulation services...</string>
+    <string name="nfc_offhost_uicc_transaction_event_emulator_help">Switch application to background before starting tests. Successful transaction event will switch application to foreground with transaction event data.</string>
 
     <!-- Strings for Sensor Test Activities -->
     <string name="snsr_device_admin_receiver">Sensor Tests Device Admin Receiver</string>
@@ -1191,6 +1224,7 @@
     <string name="snsr_test_play_sound">A sound will be played once the verification is complete...</string>
     <string name="snsr_no_interaction">Leave the device on top of a flat surface.</string>
     <string name="snsr_interaction_needed">Once the test begins, you will have to wave your hand over the front of the device.</string>
+    <string name="snsr_interaction_needed_prox">Once the test begins, if possible, flip the device so it is face down on a surface, then flip back to face up after a second or two. Otherwise, wave your hand over the front of the device.</string>
     <string name="snsr_device_steady">Keep the device steady.</string>
     <string name="snsr_keep_device_rotating_clockwise">Once the test begins, you will have to keep rotating the device clockwise.</string>
     <string name="snsr_wait_for_user">Press \'Next\' to continue.</string>
@@ -1696,6 +1730,11 @@
     <string name="wifi_setup_error">Test failed with set up error.</string>
     <string name="wifi_unexpected_error">Test failed with unexpected error. Check logcat.</string>
 
+    <string name="wifi_ssid_label">SSID for network</string>
+    <string name="wifi_psk_label">Passphrase for network (leave empty for open network)</string>
+    <string name="wifi_start_test_label">Start test</string>
+
+    <string name="wifi_status_need_psk">Need a psk network for this test.</string>
     <string name="wifi_status_initiating_scan">Initiating scan.</string>
     <string name="wifi_status_scan_failure">Unable to initiate scan or find any open network in scan results.</string>
     <string name="wifi_status_connected_to_other_network">Connected to some other network on the device. Please ensure that there is no saved networks on the device.</string>
@@ -2145,6 +2184,35 @@
         dismiss them.
     </string>
     <string name="msg_extras_preserved">Check that Message extras Bundle was preserved.</string>
+    <string name="qs_media_player_title">QS Media Controls Test</string>
+    <string name="qs_media_player_test">Media Controls Test</string>
+    <string name="qs_media_player_info">This test checks that media controls appear in the
+        Quick Settings shade for applications that post a media style notification.</string>
+    <string name="qs_media_player_song_and_artist">Fully expand Quick Settings and look at the media
+        player controls for the CTS Verifier app.
+        Check that the media player contains the strings "Song" and "Artist".
+    </string>
+    <string name="qs_media_player_album_art">Open Quick Settings and look at the media player
+        controls for the CTS Verifier app.
+        Check that the media player contains a solid yellow image.
+    </string>
+    <string name="qs_media_player_progress_bar">Fully expand Quick Settings and look at the media
+        player controls for the CTS Verifier app.
+        Check that the media player contains a progress bar showing 6 seconds have elapsed of a
+        one minute long track.
+    </string>
+    <string name="qs_media_player_actions">Fully expand Quick Settings and look at the media player
+        controls for the CTS Verifier app.
+        Check that icons appear for rewind, previous track, pause, next track and fast forward.
+    </string>
+    <string name="qs_media_player_compact_actions">Open Notification Shade and look at the media player
+        controls for the CTS Verifier app.
+        Check that icons appear for only previous track, pause and next track.
+    </string>
+    <string name="qs_media_player_output_switcher">Open Quick Settings and look at the media player
+        controls for the CTS Verifier app. Click on the button showing that the media is playing
+        on the phone speaker. Check that the Output Switcher opens.
+    </string>
     <string name="tile_service_name">Tile Service for CTS Verifier</string>
     <string name="tiles_test">Tile Service Test</string>
     <string name="tiles_info">This test checks that a Tile Service added by a third party
@@ -4686,6 +4754,7 @@
     <string name="profileLabel">Profile</string>
     <string name="connectedPeripheral">Connected Peripheral</string>
     <string name="audio_uap_attribs_test">USB Audio Peripheral Attributes Test</string>
+    <string name="audio_uap_notifications_test">USB Audio Peripheral Notifications Test</string>
     <string name="uapPeripheralProfileStatus">Peripheral Profile Status</string>
     <string name="audio_uap_play_test">USB Audio Peripheral Play Test</string>
     <string name="uapPlayTestInstructions">Connect the USB Audio Interface Peripheral and press the
@@ -4704,6 +4773,15 @@
     <string name="uapButtonTestInstructions">Connect the USB Audio headset with buttons
         and press each transport/media button in turn.</string>
 
+     <string name="uapNotificationsTestInfo">
+         This test verifies that the correct notifications and Intents are sent when a USB audio
+         peripheral is connected to the Android device. First USB headset peripheral should be
+         connected and the test will acknowledge the USB headset device notifications and plug intent.
+         Next a non-headset / headphones USB peripheral should be connected and the test will
+         acknowlege the USB device notifications. If all notifications and Intents are received
+         the test passes.
+     </string>
+
     <string name="audio_usb_restrict_record_test">USB Audio Restrict Record Access Test</string>
     <string name="audio_usb_restrict_record_entry">
         This test checks that the appropriate warning message is displayed when an app which has
diff --git a/apps/CtsVerifier/res/xml/uicc_transaction_event_aid_list.xml b/apps/CtsVerifier/res/xml/uicc_transaction_event_aid_list.xml
new file mode 100644
index 0000000..91042d1
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/uicc_transaction_event_aid_list.xml
@@ -0,0 +1,11 @@
+<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/UiccTransactionEventService"
+    android:requireDeviceUnlock="false">
+    <aid-group
+        android:category="other"
+        android:description="@string/UiccTransactionEventService" >
+
+        <aid-filter android:name="A000000476416E64726F696443545341"/>
+    </aid-group>
+
+</offhost-apdu-service>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
new file mode 100644
index 0000000..c8481fe
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2020 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.verifier.audio;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
+
+@CddTest(requirement="7.8.2.2/H-2-1,H-3-1,H-4-2,H-4-3,H-4-4,H-4-5")
+public class USBAudioPeripheralNotificationsTest extends PassFailButtons.Activity {
+    private static final String
+            TAG = USBAudioPeripheralNotificationsTest.class.getSimpleName();
+
+    private AudioManager    mAudioManager;
+
+    private TextView mHeadsetInName;
+    private TextView mHeadsetOutName;
+    private TextView mUsbDeviceInName;
+    private TextView mUsbDeviceOutName;
+
+    // private TextView mHeadsetPlugText;
+    private TextView mHeadsetPlugMessage;
+
+    // Intents
+    private HeadsetPlugReceiver mHeadsetPlugReceiver;
+    private boolean mPlugIntentReceived;
+
+    // Device
+    private AudioDeviceInfo mUsbHeadsetInInfo;
+    private AudioDeviceInfo mUsbHeadsetOutInfo;
+    private AudioDeviceInfo mUsbDeviceInInfo;
+    private AudioDeviceInfo mUsbDeviceOutInfo;
+
+    private boolean mUsbHeadsetInReceived;
+    private boolean mUsbHeadsetOutReceived;
+    private boolean mUsbDeviceInReceived;
+    private boolean mUsbDeviceOutReceived;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.uap_notifications_layout);
+
+        mHeadsetInName = (TextView)findViewById(R.id.uap_messages_headset_in_name);
+        mHeadsetOutName = (TextView)findViewById(R.id.uap_messages_headset_out_name);
+
+        mUsbDeviceInName = (TextView)findViewById(R.id.uap_messages_usb_device_in_name);
+        mUsbDeviceOutName = (TextView)findViewById(R.id.uap_messages_usb_device__out_name);
+
+        mHeadsetPlugMessage = (TextView)findViewById(R.id.uap_messages_plug_message);
+
+        mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+        mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+        mHeadsetPlugReceiver = new HeadsetPlugReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_HEADSET_PLUG);
+        registerReceiver(mHeadsetPlugReceiver, filter);
+
+        setInfoResources(R.string.audio_uap_notifications_test, R.string.uapNotificationsTestInfo,
+                -1);
+
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+    }
+
+    //
+    // UI
+    //
+    private void showConnectedDevices() {
+        if (mUsbHeadsetInInfo != null) {
+            mHeadsetInName.setText(
+                    "Headset INPUT Connected " + mUsbHeadsetInInfo.getProductName());
+        } else {
+            mHeadsetInName.setText("");
+        }
+
+        if (mUsbHeadsetOutInfo != null) {
+            mHeadsetOutName.setText(
+                    "Headset OUTPUT Connected " + mUsbHeadsetOutInfo.getProductName());
+        } else {
+            mHeadsetOutName.setText("");
+        }
+
+        if (mUsbDeviceInInfo != null) {
+            mUsbDeviceInName.setText(
+                    "USB DEVICE INPUT Connected " + mUsbDeviceInInfo.getProductName());
+        } else {
+            mUsbDeviceInName.setText("");
+        }
+
+        if (mUsbDeviceOutInfo != null) {
+            mUsbDeviceOutName.setText(
+                    "USB DEVICE OUTPUT Connected " + mUsbDeviceOutInfo.getProductName());
+        } else {
+            mUsbDeviceOutName.setText("");
+        }
+    }
+
+    private void reportPlugIntent(Intent intent) {
+        // [ 7.8 .2.2/H-2-1] MUST broadcast Intent ACTION_HEADSET_PLUG with "microphone" extra
+        // set to 0 when the USB audio terminal types 0x0302 is detected.
+        // [ 7.8 .2.2/H-3-1] MUST broadcast Intent ACTION_HEADSET_PLUG with "microphone" extra
+        // set to 1 when the USB audio terminal types 0x0402 is detected, they:
+        mPlugIntentReceived = true;
+
+        // state - 0 for unplugged, 1 for plugged.
+        // name - Headset type, human readable string
+        // microphone - 1 if headset has a microphone, 0 otherwise
+        int state = intent.getIntExtra("state", -1);
+        if (state != -1) {
+
+            StringBuilder sb = new StringBuilder();
+            sb.append("ACTION_HEADSET_PLUG received - " + (state == 0 ? "Unplugged" : "Plugged"));
+
+            String name = intent.getStringExtra("name");
+            if (name != null) {
+                sb.append(" - " + name);
+            }
+
+            int hasMic = intent.getIntExtra("microphone", 0);
+            if (hasMic == 1) {
+                sb.append(" [mic]");
+            }
+
+            mHeadsetPlugMessage.setText(sb.toString());
+        }
+
+        getReportLog().addValue(
+                "ACTION_HEADSET_PLUG Intent Received. State: ",
+                state,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        getPassButton().setEnabled(calculatePass());
+    }
+
+    //
+    // Test Status
+    //
+    private boolean calculatePass() {
+        return mUsbHeadsetInReceived && mUsbHeadsetOutReceived &&
+                mUsbDeviceInReceived && mUsbDeviceOutReceived &&
+                mPlugIntentReceived;
+    }
+
+    //
+    // Devices
+    //
+    private void scanDevices(AudioDeviceInfo[] devices) {
+        mUsbHeadsetInInfo = mUsbHeadsetOutInfo =
+                mUsbDeviceInInfo = mUsbDeviceOutInfo = null;
+
+        for (AudioDeviceInfo devInfo : devices) {
+            if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
+                if (devInfo.isSource()) {
+                    // [ 7.8 .2.2/H-4-3] MUST list a device of type AudioDeviceInfo.TYPE_USB_HEADSET
+                    // and role isSource() if the USB audio terminal type field is 0x0402.
+                    mUsbHeadsetInReceived = true;
+                    mUsbHeadsetInInfo = devInfo;
+                    getReportLog().addValue(
+                            "USB Headset connected - INPUT",
+                            0,
+                            ResultType.NEUTRAL,
+                            ResultUnit.NONE);
+                } else if (devInfo.isSink()) {
+                    // [ 7.8 .2.2/H-4-2] MUST list a device of type AudioDeviceInfo.TYPE_USB_HEADSET
+                    // and role isSink() if the USB audio terminal type field is 0x0402.
+                    mUsbHeadsetOutReceived = true;
+                    mUsbHeadsetOutInfo = devInfo;
+                    getReportLog().addValue(
+                            "USB Headset connected - OUTPUT",
+                            0,
+                            ResultType.NEUTRAL,
+                            ResultUnit.NONE);
+                }
+            } else if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE) {
+                if (devInfo.isSource()) {
+                    // [ 7.8 .2.2/H-4-5] MUST list a device of type AudioDeviceInfo.TYPE_USB_DEVICE
+                    // and role isSource() if the USB audio terminal type field is 0x604.
+                    mUsbDeviceInReceived = true;
+                    mUsbDeviceInInfo = devInfo;
+                    getReportLog().addValue(
+                            "USB Device connected - INPUT",
+                            0,
+                            ResultType.NEUTRAL,
+                            ResultUnit.NONE);
+                } else if (devInfo.isSink()) {
+                    // [ 7.8 .2.2/H-4-4] MUST list a device of type AudioDeviceInfo.TYPE_USB_DEVICE
+                    // and role isSink() if the USB audio terminal type field is 0x603.
+                    mUsbDeviceOutReceived = true;
+                    mUsbDeviceOutInfo = devInfo;
+                    getReportLog().addValue(
+                            "USB Headset connected - OUTPUT",
+                            0,
+                            ResultType.NEUTRAL,
+                            ResultUnit.NONE);
+                }
+            }
+
+            if (mUsbHeadsetInInfo != null &&
+                    mUsbHeadsetOutInfo != null &&
+                    mUsbDeviceInInfo != null &&
+                    mUsbDeviceOutInfo != null) {
+                break;
+            }
+        }
+
+
+        showConnectedDevices();
+        getPassButton().setEnabled(calculatePass());
+    }
+
+    private class ConnectListener extends AudioDeviceCallback {
+        /*package*/ ConnectListener() {}
+
+        //
+        // AudioDevicesManager.OnDeviceConnectionListener
+        //
+        @Override
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            Log.i(TAG, "onAudioDevicesAdded() num:" + addedDevices.length);
+
+            scanDevices(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            Log.i(TAG, "onAudioDevicesRemoved() num:" + removedDevices.length);
+
+            scanDevices(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+    }
+
+    // Intents
+    private class HeadsetPlugReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reportPlugIntent(intent);
+        }
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
index c710490..9ddb5a0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
@@ -24,6 +24,8 @@
 import com.android.cts.verifier.nfc.hce.HceReaderTestActivity;
 import com.android.cts.verifier.nfc.hcef.HceFEmulatorTestActivity;
 import com.android.cts.verifier.nfc.hcef.HceFReaderTestActivity;
+import com.android.cts.verifier.nfc.offhost.OffhostUiccEmulatorTestActivity;
+import com.android.cts.verifier.nfc.offhost.OffhostUiccReaderTestActivity;
 
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -97,6 +99,16 @@
                     new Intent(this, HceFEmulatorTestActivity.class), null));
         }
 
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_offhost_uicc));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_reader_tests,
+                    OffhostUiccReaderTestActivity.class.getName(),
+                    new Intent(this, OffhostUiccReaderTestActivity.class), null));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_emulator_tests,
+                    OffhostUiccEmulatorTestActivity.class.getName(),
+                    new Intent(this, OffhostUiccEmulatorTestActivity.class), null));
+        }
+
         setTestListAdapter(adapter);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
index 698948b..a324a4e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
@@ -25,6 +25,9 @@
     public static final String LARGE_NUM_AIDS_PREFIX = "F00102030414";
     public static final String LARGE_NUM_AIDS_POSTFIX ="81";
 
+    public static final String TRANSACTION_EVENT_AID = "A000000476416E64726F696443545341";
+    public static final String HCI_CMD = "0025000000";
+
     public static void enableComponent(PackageManager pm, ComponentName component) {
         pm.setComponentEnabledSetting(
                 component,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccEmulatorTestActivity.java
new file mode 100644
index 0000000..119a63b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccEmulatorTestActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+/** Activity that lists all the NFC Offhost-UICC emulator tests. */
+public class OffhostUiccEmulatorTestActivity extends PassFailButtons.TestListActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_offhost_uicc_emulator_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_offhost_uicc_emulator_tests));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event1_emulator,
+                    UiccTransactionEvent1EmulatorActivity.class.getName(),
+                    new Intent(this, UiccTransactionEvent1EmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event2_emulator,
+                    UiccTransactionEvent2EmulatorActivity.class.getName(),
+                    new Intent(this, UiccTransactionEvent2EmulatorActivity.class), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event3_emulator,
+                    UiccTransactionEvent3EmulatorActivity.class.getName(),
+                    new Intent(this, UiccTransactionEvent3EmulatorActivity.class), null));
+
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccReaderTestActivity.java
new file mode 100644
index 0000000..09db107
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/OffhostUiccReaderTestActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+/** Activity that lists all the NFC Offhost-UICC reader tests. */
+public class OffhostUiccReaderTestActivity extends PassFailButtons.TestListActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_offhost_uicc_reader_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_offhost_uicc_reader_tests));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event1_reader,
+                    getString(R.string.nfc_offhost_uicc_transaction_event1_reader),
+                    UiccTransactionEvent1EmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event2_reader,
+                    getString(R.string.nfc_offhost_uicc_transaction_event2_reader),
+                    UiccTransactionEvent2EmulatorActivity.buildReaderIntent(this), null));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_offhost_uicc_transaction_event3_reader,
+                    getString(R.string.nfc_offhost_uicc_transaction_event3_reader),
+                    UiccTransactionEvent3EmulatorActivity.buildReaderIntent(this), null));
+
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/SimpleOffhostReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/SimpleOffhostReaderActivity.java
new file mode 100644
index 0000000..1856601
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/SimpleOffhostReaderActivity.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.nfc.tech.IsoDep;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+import com.android.cts.verifier.nfc.hce.CommandApdu;
+
+public class SimpleOffhostReaderActivity extends PassFailButtons.Activity implements ReaderCallback,
+        OnItemSelectedListener {
+    public static final String PREFS_NAME = "OffhostTypePrefs";
+
+    public static final String TAG = "SimpleOffhostReaderActivity";
+    public static final String EXTRA_APDUS = "apdus";
+    public static final String EXTRA_RESPONSES = "responses";
+    public static final String EXTRA_LABEL = "label";
+    public static final String EXTRA_DESELECT = "deselect";
+
+    NfcAdapter mAdapter;
+    CommandApdu[] mApdus;
+    String[] mResponses;
+    String mLabel;
+    boolean mDeselect;
+
+    TextView mTextView;
+    Spinner mSpinner;
+    SharedPreferences mPrefs;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.nfc_hce_reader);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mLabel = getIntent().getStringExtra(EXTRA_LABEL);
+        mDeselect = getIntent().getBooleanExtra(EXTRA_DESELECT, false);
+        setTitle(mLabel);
+
+        mAdapter = NfcAdapter.getDefaultAdapter(this);
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+        mTextView.setText(R.string.nfc_offhost_uicc_type_selection);
+
+        Spinner spinner = (Spinner) findViewById(R.id.type_ab_selection);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.nfc_types_array, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spinner.setAdapter(adapter);
+        spinner.setOnItemSelectedListener(this);
+
+        mPrefs = getSharedPreferences(PREFS_NAME, 0);
+        boolean isTypeB = mPrefs.getBoolean("typeB", false);
+        if (isTypeB) {
+            spinner.setSelection(1);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A |
+                NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
+        Intent intent = getIntent();
+        Parcelable[] apdus = intent.getParcelableArrayExtra(EXTRA_APDUS);
+        if (apdus != null) {
+            mApdus = new CommandApdu[apdus.length];
+            for (int i = 0; i < apdus.length; i++) {
+                mApdus[i] = (CommandApdu) apdus[i];
+            }
+        } else {
+            mApdus = null;
+        }
+        mResponses = intent.getStringArrayExtra(EXTRA_RESPONSES);
+    }
+
+    @Override
+    public void onTagDiscovered(Tag tag) {
+        final StringBuilder sb = new StringBuilder();
+        IsoDep isoDep = IsoDep.get(tag);
+        if (isoDep == null) {
+            // TODO dialog box
+            return;
+        }
+
+        try {
+            isoDep.connect();
+            isoDep.setTimeout(5000);
+            int count = 0;
+            boolean success = true;
+            long startTime = System.currentTimeMillis();
+            for (CommandApdu apdu: mApdus) {
+                sb.append("Request APDU:\n");
+                sb.append(apdu.getApdu() + "\n\n");
+                long apduStartTime = System.currentTimeMillis();
+                byte[] response = isoDep.transceive(HceUtils.hexStringToBytes(apdu.getApdu()));
+                long apduEndTime = System.currentTimeMillis();
+                sb.append("Response APDU (in " + Long.toString(apduEndTime - apduStartTime) +
+                        " ms):\n");
+                sb.append(HceUtils.getHexBytes(null, response));
+
+                sb.append("\n\n\n");
+                boolean wildCard = "*".equals(mResponses[count]);
+                byte[] expectedResponse = HceUtils.hexStringToBytes(mResponses[count]);
+                Log.d(TAG, HceUtils.getHexBytes("APDU response: ", response));
+                if (!wildCard && !Arrays.equals(response, expectedResponse)) {
+                    Log.d(TAG, "Unexpected APDU response: " + HceUtils.getHexBytes("", response));
+                    success = false;
+                    break;
+                }
+                count++;
+            }
+
+            if (mDeselect) {
+                mAdapter.disableReaderMode(this);
+            }
+
+            if (success) {
+                sb.insert(0, "Total APDU exchange time: " +
+                        Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                sb.insert(0, "FAIL. Total APDU exchange time: " +
+                        Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText(sb.toString());
+                        AlertDialog.Builder builder = new AlertDialog.Builder(SimpleOffhostReaderActivity.this);
+                        builder.setTitle("Test failed");
+                        builder.setMessage("An unexpected response APDU was received, or no APDUs were received at all.");
+                        builder.setPositiveButton("OK", null);
+                        builder.show();
+                    }
+                });
+            }
+
+        } catch (IOException e) {
+            sb.insert(0, "Error while reading: (did you keep the devices in range?)\nPlease try again\n.");
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mTextView.setText(sb.toString());
+                }
+            });
+        } finally {
+        }
+    }
+
+    @Override
+    public void onItemSelected(AdapterView<?> parent, View view, int position,
+            long id) {
+        if (position == 0) {
+            // Type-A
+            mAdapter.disableReaderMode(this);
+            mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A |
+                NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
+            SharedPreferences.Editor editor = mPrefs.edit();
+            editor.putBoolean("typeB", false);
+            editor.commit();
+        } else {
+            // Type-B
+            mAdapter.disableReaderMode(this);
+            mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_B |
+                    NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
+            SharedPreferences.Editor editor = mPrefs.edit();
+            editor.putBoolean("typeB", true);
+            editor.commit();
+        }
+    }
+
+    @Override
+    public void onNothingSelected(AdapterView<?> parent) {
+    }
+
+    @Override
+    public String getTestId() {
+        return mLabel;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java
new file mode 100644
index 0000000..037dd7f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+
+public class UiccTransactionEvent1EmulatorActivity extends PassFailButtons.Activity {
+    static final String TAG = "UiccTransactionEvent1EmulatorActivity";
+
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+        mTextView.setText(R.string.nfc_offhost_uicc_transaction_event_emulator_help);
+
+        initProcess();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+
+        setIntent(intent);
+        initProcess();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleOffhostReaderActivity.class);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_APDUS,
+                UiccTransactionEvent1Service.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_RESPONSES,
+                UiccTransactionEvent1Service.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_offhost_uicc_transaction_event1_reader));
+        return readerIntent;
+    }
+
+    private void initProcess() {
+
+        Bundle bundle = getIntent().getExtras();
+        if(bundle != null){
+            byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
+            if(transactionData != null){
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Pass - NFC Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data:" + HceUtils.getHexBytes(null, transactionData));
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Fail - Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data: null");
+                        getPassButton().setEnabled(false);
+                    }
+                });
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1Service.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1Service.java
new file mode 100644
index 0000000..5a1fd86
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1Service.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.content.ComponentName;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+import com.android.cts.verifier.nfc.hce.CommandApdu;
+
+public class UiccTransactionEvent1Service {
+    public static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+                    UiccTransactionEvent1Service.class.getName());
+
+    public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.TRANSACTION_EVENT_AID, true),
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "9000",
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java
new file mode 100644
index 0000000..6f257af
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+
+public class UiccTransactionEvent2EmulatorActivity extends PassFailButtons.Activity {
+    static final String TAG = "UiccTransactionEvent2EmulatorActivity";
+
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+        mTextView.setText(R.string.nfc_offhost_uicc_transaction_event_emulator_help);
+
+        initProcess();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+
+        setIntent(intent);
+        initProcess();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleOffhostReaderActivity.class);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_APDUS,
+                UiccTransactionEvent2Service.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_RESPONSES,
+                UiccTransactionEvent2Service.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_offhost_uicc_transaction_event2_reader));
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_DESELECT, true);
+        return readerIntent;
+    }
+
+    private void initProcess() {
+        Bundle bundle = getIntent().getExtras();
+        if(bundle != null){
+            byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
+            if(transactionData != null){
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Pass - NFC Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data:" + HceUtils.getHexBytes(null, transactionData));
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Fail - Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data: null");
+                        getPassButton().setEnabled(false);
+                    }
+                });
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2Service.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2Service.java
new file mode 100644
index 0000000..2da9a1d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2Service.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.content.ComponentName;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+import com.android.cts.verifier.nfc.hce.CommandApdu;
+
+public class UiccTransactionEvent2Service {
+    public static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+                    UiccTransactionEvent2Service.class.getName());
+
+    public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.TRANSACTION_EVENT_AID, true),
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "9000",
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java
new file mode 100644
index 0000000..d79674d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+
+public class UiccTransactionEvent3EmulatorActivity extends PassFailButtons.Activity {
+    static final String TAG = "UiccTransactionEvent3EmulatorActivity";
+
+    TextView mTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+        mTextView.setText(R.string.nfc_offhost_uicc_transaction_event_emulator_help);
+
+        initProcess();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setTextSize(12.0f);
+
+        setIntent(intent);
+        initProcess();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    public static Intent buildReaderIntent(Context context) {
+        Intent readerIntent = new Intent(context, SimpleOffhostReaderActivity.class);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_APDUS,
+                UiccTransactionEvent3Service.APDU_COMMAND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_RESPONSES,
+                UiccTransactionEvent3Service.APDU_RESPOND_SEQUENCE);
+        readerIntent.putExtra(SimpleOffhostReaderActivity.EXTRA_LABEL,
+                context.getString(R.string.nfc_offhost_uicc_transaction_event3_reader));
+        return readerIntent;
+    }
+
+    private void initProcess() {
+        Bundle bundle = getIntent().getExtras();
+        if(bundle != null){
+            byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
+            if(transactionData != null){
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Pass - NFC Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data:" + HceUtils.getHexBytes(null, transactionData));
+                        getPassButton().setEnabled(true);
+                    }
+                });
+            } else {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mTextView.setText("Fail - Action:" + getIntent().getAction() + " uri:" + getIntent().getDataString()
+                            + " data: null");
+                        getPassButton().setEnabled(false);
+                    }
+                });
+            }
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3Service.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3Service.java
new file mode 100644
index 0000000..a273d27
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3Service.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.content.ComponentName;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+import com.android.cts.verifier.nfc.hce.CommandApdu;
+
+public class UiccTransactionEvent3Service {
+    public static final ComponentName COMPONENT =
+            new ComponentName("com.android.cts.verifier",
+                    UiccTransactionEvent3Service.class.getName());
+
+    public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
+        HceUtils.buildSelectApdu(HceUtils.TRANSACTION_EVENT_AID, true),
+        HceUtils.buildCommandApdu(HceUtils.HCI_CMD, true),
+    };
+
+    public static final String[] APDU_RESPOND_SEQUENCE = {
+        "9000",
+        "9000"
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEventReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEventReceiver.java
new file mode 100644
index 0000000..af8cca7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEventReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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.verifier.nfc.offhost;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.util.Log;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+
+import java.util.Arrays;
+
+public class UiccTransactionEventReceiver extends BroadcastReceiver {
+    final byte[] transactionData1 = new byte[]{0x54};
+    final byte[] transactionData2 = new byte[]{0x52};
+    final byte[] transactionData3 = new byte[]{0x02};
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d("UiccTransactionEventReceiver", "onReceive ");
+        if(intent != null){
+            Log.d("UiccTransactionEventReceiver", "uri : " + intent.getDataString());
+            byte[] transactionData = intent.getByteArrayExtra(NfcAdapter.EXTRA_DATA);
+            Log.d("UiccTransactionEventReceiver", "data " + HceUtils.getHexBytes(null, transactionData));
+            if (Arrays.equals(transactionData, transactionData1)) {
+                intent.setClassName(context.getPackageName(), UiccTransactionEvent1EmulatorActivity.class.getName());
+            } else if (Arrays.equals(transactionData, transactionData2)) {
+                intent.setClassName(context.getPackageName(), UiccTransactionEvent2EmulatorActivity.class.getName());
+            } else if (Arrays.equals(transactionData, transactionData3)) {
+                intent.setClassName(context.getPackageName(), UiccTransactionEvent3EmulatorActivity.class.getName());
+            }
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            context.startActivity(intent);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java
new file mode 100644
index 0000000..eb6a681
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java
@@ -0,0 +1,165 @@
+/*
+ * 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.verifier.notifications;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for media player shown in quick setting when media style notification is posted.
+ */
+public class MediaPlayerVerifierActivity extends InteractiveVerifierActivity {
+
+    // Pieces of the media session.
+    private static final String SESSION_KEY = "Session";
+    private static final String SESSION_TITLE = "Song";
+    private static final String SESSION_ARTIST = "Artist";
+    private static final long SESSION_DURATION = 60000L;
+
+    // Pieces of the media style notification.
+    private static final String TITLE = "Media-style Notification";
+    private static final String TEXT = "Notification for a test media session";
+    private static final String CHANNEL_ID = "MediaPlayerVerifierActivity";
+
+    private MediaSession mSession;
+    private NotificationManager mManager;
+    private Notification.Builder mBuilder;
+
+    @Override
+    public List<InteractiveTestCase> createTestItems() {
+        List<InteractiveTestCase> cases = new ArrayList<>();
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_song_and_artist));
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_album_art));
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_progress_bar));
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_actions));
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_output_switcher));
+        cases.add(new MediaPlayerTestCase(R.string.qs_media_player_compact_actions));
+        return cases;
+    }
+
+    @Override
+    public int getInstructionsResource() {
+        return R.string.qs_media_player_info;
+    }
+
+    @Override
+    public int getTitleResource() {
+        return R.string.qs_media_player_test;
+    }
+
+    private class MediaPlayerTestCase extends InteractiveTestCase {
+        private final int mDescriptionResId;
+
+        MediaPlayerTestCase(int resId) {
+            mDescriptionResId = resId;
+        }
+
+        @Override
+        protected void setUp() {
+            postMediaStyleNotification();
+            status = READY;
+        }
+
+        @Override
+        protected void tearDown() {
+            cancelMediaStyleNotification();
+        }
+
+        @Override
+        protected View inflate(ViewGroup parent) {
+            return createPassFailItem(parent, mDescriptionResId);
+        }
+
+        @Override
+        protected void test() {
+            status = WAIT_FOR_USER;
+            next();
+        }
+    }
+
+    private void postMediaStyleNotification() {
+        mManager = this.getSystemService(NotificationManager.class);
+        mSession = new MediaSession(this, SESSION_KEY);
+
+        // create a solid color bitmap to use as album art in media metadata
+        Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
+        new Canvas(bitmap).drawColor(Color.YELLOW);
+
+        // set up media session with metadata and playback state
+        mSession.setMetadata(new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+                .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+                .putLong(MediaMetadata.METADATA_KEY_DURATION, SESSION_DURATION)
+                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)
+                .build());
+        mSession.setPlaybackState(new PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
+                .setActions(PlaybackState.ACTION_SEEK_TO | PlaybackState.ACTION_PLAY |
+                        PlaybackState.ACTION_PAUSE)
+                .build());
+
+        // set up notification builder
+        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID,
+                NotificationManager.IMPORTANCE_LOW);
+        mManager.createNotificationChannel(channel);
+        mBuilder = new Notification.Builder(this, CHANNEL_ID)
+                .setContentTitle(TITLE).setContentText(TEXT)
+                .setSmallIcon(R.drawable.ic_android)
+                .setStyle(new Notification.MediaStyle()
+                        .setShowActionsInCompactView(1, 2, 3)
+                        .setMediaSession(mSession.getSessionToken()))
+                .setColor(Color.BLUE)
+                .setColorized(true)
+                .addAction(android.R.drawable.ic_media_rew, "rewind", null)
+                .addAction(android.R.drawable.ic_media_previous, "previous track", null)
+                .addAction(android.R.drawable.ic_media_pause, "pause", null)
+                .addAction(android.R.drawable.ic_media_next, "next track", null)
+                .addAction(android.R.drawable.ic_media_ff, "fast forward", null);
+
+        mSession.setActive(true);
+        mManager.notify(1, mBuilder.build());
+    }
+
+    private void cancelMediaStyleNotification() {
+        if (mSession != null) {
+            mSession.release();
+            mSession = null;
+        }
+        if (mManager != null) {
+            mManager.cancelAll();
+            mManager.deleteNotificationChannel(CHANNEL_ID);
+            mManager = null;
+        }
+    }
+}
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 9689193..9a1ed4a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.verifier.sensors;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
 
@@ -24,6 +25,7 @@
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventBasicVerification;
 
 import java.util.concurrent.TimeUnit;
 
@@ -81,26 +83,22 @@
                 R.string.snsr_batching_walking_needed);
     }
 
+    @CddTest(requirement="7.3.8/C-1-1,C-1-2")
     @SuppressWarnings("unused")
     public String testProximity_batching() throws Throwable {
-        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_PROXIMITY)) {
-            return null;
-        }
         return runBatchTest(
                 Sensor.TYPE_PROXIMITY,
                 REPORT_LATENCY_10_SEC,
-                R.string.snsr_interaction_needed);
+                R.string.snsr_interaction_needed_prox);
     }
 
+    @CddTest(requirement="7.3.8/C-1-1,C-1-2")
     @SuppressWarnings("unused")
     public String testProximity_flush() throws Throwable {
-        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_PROXIMITY)) {
-            return null;
-        }
         return runFlushTest(
                 Sensor.TYPE_PROXIMITY,
                 REPORT_LATENCY_10_SEC,
-                R.string.snsr_interaction_needed);
+                R.string.snsr_interaction_needed_prox);
     }
 
     @SuppressWarnings("unused")
@@ -134,6 +132,13 @@
         int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
         TestSensorOperation operation =
                 TestSensorOperation.createOperation(environment, testDurationSec,TimeUnit.SECONDS);
+
+        // Expect at least 2 events (for on-change: initial value + changed value; for step sensors
+        // multiple values for walking).
+        EventBasicVerification verification =
+                new EventBasicVerification(2 /* expectedMinNumEvent */, environment.getSensor());
+        operation.addVerification(verification);
+
         return executeTest(operation);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HingeAngleTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HingeAngleTestActivity.java
index aebc6cf..cdde74e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HingeAngleTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HingeAngleTestActivity.java
@@ -52,7 +52,7 @@
      *  - Duplicate values are not seen back-to-back.
      */
     private String verifyMeasurements(int instructionsResId) throws Throwable {
-        Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE, true);
+        Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);
         TestSensorEnvironment environment = new TestSensorEnvironment(
                 getApplicationContext(),
                 wakeUpSensor,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestActivity.java
index 4db3766..8c4466d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestActivity.java
@@ -19,8 +19,13 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
@@ -32,11 +37,15 @@
  */
 public abstract class BaseTestActivity extends PassFailButtons.Activity implements
         BaseTestCase.Listener {
+    private static final String TAG = "BaseTestActivity";
     /*
      * Handles to GUI elements.
      */
     private TextView mWifiInfo;
     private ProgressBar mWifiProgress;
+    private Button mStartButton;
+    private EditText mSsidEditText;
+    private EditText mPskEditText;
 
     /*
      * Test case to be executed
@@ -45,6 +54,9 @@
 
     private Handler mHandler = new Handler();
 
+    private String mSsidValue;
+    private String mPskValue;
+
     protected abstract BaseTestCase getTestCase(Context context);
 
     @Override
@@ -57,6 +69,39 @@
         // Get UI component.
         mWifiInfo = (TextView) findViewById(R.id.wifi_info);
         mWifiProgress = (ProgressBar) findViewById(R.id.wifi_progress);
+        mStartButton = findViewById(R.id.wifi_start_test_btn);
+        mSsidEditText = findViewById(R.id.wifi_ssid);
+        mPskEditText = findViewById(R.id.wifi_psk);
+        mSsidEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+            @Override
+            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+            @Override
+            public void afterTextChanged(Editable editable) {
+                mSsidValue = editable.toString();
+                mStartButton.setEnabled(true);
+            }
+        });
+        mPskEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+            @Override
+            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+            @Override
+            public void afterTextChanged(Editable editable) {
+                mPskValue = editable.toString();
+            }
+        });
+        mStartButton.setEnabled(false);
+        mStartButton.setOnClickListener(view -> {
+            mTestCase.start(this, mSsidValue, mPskValue == null ? "" : mPskValue);
+            mWifiProgress.setVisibility(View.VISIBLE);
+        });
 
         // Initialize test components.
         mTestCase = getTestCase(this);
@@ -66,13 +111,6 @@
     }
 
     @Override
-    protected void onStart() {
-        super.onStart();
-        mTestCase.start(this);
-        mWifiProgress.setVisibility(View.VISIBLE);
-    }
-
-    @Override
     protected void onStop() {
         super.onStop();
         mTestCase.stop();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestCase.java
index 2ab8bdb..3fc95d6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/BaseTestCase.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.verifier.wifi;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.wifi.WifiManager;
@@ -30,6 +31,7 @@
  */
 public abstract class BaseTestCase {
     private static final String TAG = "BaseTestCase";
+
     protected Context mContext;
     protected Resources mResources;
     protected Listener mListener;
@@ -40,6 +42,9 @@
     protected WifiManager mWifiManager;
     protected TestUtils mTestUtils;
 
+    protected String mSsid;
+    protected String mPsk;
+
     public BaseTestCase(Context context) {
         mContext = context;
         mResources = mContext.getResources();
@@ -95,8 +100,10 @@
      * <p>
      * Test case is executed in another thread.
      */
-    public void start(Listener listener) {
+    public void start(@NonNull Listener listener, @NonNull String ssid, @NonNull String psk) {
         mListener = listener;
+        mSsid = ssid;
+        mPsk = psk;
 
         stop();
         mHandlerThread = new HandlerThread("CtsVerifier-Wifi");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/TestUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/TestUtils.java
index dadacd3..895c423 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/TestUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/TestUtils.java
@@ -101,6 +101,8 @@
     public static final int SCAN_RESULT_TYPE_PSK = 1;
 
     @IntDef(prefix = { "SCAN_RESULT_TYPE_" }, value = {
+            SCAN_RESULT_TYPE_OPEN,
+            SCAN_RESULT_TYPE_PSK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScanResultType {}
@@ -151,7 +153,7 @@
      * scan failed or if there are networks found.
      */
     public @Nullable ScanResult startScanAndFindAnyMatchingNetworkInResults(
-            @ScanResultType int type)
+            String ssid, @ScanResultType int type)
             throws InterruptedException {
         // Start scan and wait for new results.
         if (!startScanAndWaitForResults()) {
@@ -160,14 +162,14 @@
         // Filter results to find an open network.
         List<ScanResult> scanResults = mWifiManager.getScanResults();
         for (ScanResult scanResult : scanResults) {
-            if (!TextUtils.isEmpty(scanResult.SSID)
+            if (TextUtils.equals(ssid, scanResult.SSID)
                     && !TextUtils.isEmpty(scanResult.BSSID)
                     && doesScanResultMatchType(scanResult, type)) {
-                if (DBG) Log.v(TAG, "Found open network " + scanResult);
+                if (DBG) Log.v(TAG, "Found network " + scanResult);
                 return scanResult;
             }
         }
-        Log.e(TAG, "No open networks found in scan results");
+        Log.e(TAG, "No matching network found in scan results");
         return null;
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
index 96f9547..75e5a42 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
@@ -92,6 +92,13 @@
             case NETWORK_SPECIFIER_SPECIFIC_SSID_BSSID:
                 configBuilder.setSsid(scanResult.SSID);
                 configBuilder.setBssid(MacAddress.fromString(scanResult.BSSID));
+                if (!mPsk.isEmpty()) {
+                    if (TestUtils.isScanResultForWpa2Network(scanResult)) {
+                        configBuilder.setWpa2Passphrase(mPsk);
+                    } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
+                        configBuilder.setWpa3Passphrase(mPsk);
+                    }
+                }
                 break;
             case NETWORK_SPECIFIER_PATTERN_SSID_BSSID:
                 String ssidPrefix = scanResult.SSID.substring(0, scanResult.SSID.length() - 1);
@@ -99,6 +106,13 @@
                 configBuilder.setSsidPattern(
                         new PatternMatcher(ssidPrefix, PatternMatcher.PATTERN_PREFIX));
                 configBuilder.setBssidPattern(MacAddress.fromString(scanResult.BSSID), bssidMask);
+                if (!mPsk.isEmpty()) {
+                    if (TestUtils.isScanResultForWpa2Network(scanResult)) {
+                        configBuilder.setWpa2Passphrase(mPsk);
+                    } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
+                        configBuilder.setWpa3Passphrase(mPsk);
+                    }
+                }
                 break;
             case NETWORK_SPECIFIER_UNAVAILABLE_SSID_BSSID:
                 String ssid = UNAVAILABLE_SSID;
@@ -110,6 +124,13 @@
                 }
                 configBuilder.setSsid(UNAVAILABLE_SSID);
                 configBuilder.setBssid(bssid);
+                if (!mPsk.isEmpty()) {
+                    if (TestUtils.isScanResultForWpa2Network(scanResult)) {
+                        configBuilder.setWpa2Passphrase(mPsk);
+                    } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
+                        configBuilder.setWpa3Passphrase(mPsk);
+                    }
+                }
                 break;
             case NETWORK_SPECIFIER_INVALID_CREDENTIAL:
                 configBuilder.setSsid(scanResult.SSID);
@@ -136,11 +157,14 @@
 
     @Override
     protected boolean executeTest() throws InterruptedException {
-        // Step: Scan and find any open network around.
-        if (DBG) Log.v(TAG, "Scan and find an open network");
+        if (mNetworkSpecifierType == NETWORK_SPECIFIER_INVALID_CREDENTIAL && mPsk.isEmpty()) {
+            setFailureReason(mContext.getString(R.string.wifi_status_need_psk));
+            return false;
+        }
+        // Step: Scan and find the network around.
+        if (DBG) Log.v(TAG, "Scan and find the network: " + mSsid);
         ScanResult testNetwork = mTestUtils.startScanAndFindAnyMatchingNetworkInResults(
-                mNetworkSpecifierType == NETWORK_SPECIFIER_INVALID_CREDENTIAL
-                        ? SCAN_RESULT_TYPE_PSK : SCAN_RESULT_TYPE_OPEN);
+                mSsid, mPsk.isEmpty() ? SCAN_RESULT_TYPE_OPEN : SCAN_RESULT_TYPE_PSK);
         if (testNetwork == null) {
             setFailureReason(mContext.getString(R.string.wifi_status_scan_failure));
             return false;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
index 3d5e07a..62892d6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
@@ -67,11 +67,11 @@
 
     private final Object mLock = new Object();
     private final ScheduledExecutorService mExecutorService;
-    private final List<WifiNetworkSuggestion> mNetworkSuggestions = new ArrayList<>();
     private final WifiNetworkSuggestion.Builder mNetworkSuggestionBuilder =
             new WifiNetworkSuggestion.Builder();
 
     private ConnectivityManager mConnectivityManager;
+    private List<WifiNetworkSuggestion> mNetworkSuggestions;
     private NetworkRequest mNetworkRequest;
     private CallbackUtils.NetworkCallback mNetworkCallback;
     private ConnectionStatusListener mConnectionStatusListener;
@@ -113,11 +113,19 @@
         if (mSetRequiresAppInteraction) {
             mNetworkSuggestionBuilder.setIsAppInteractionRequired(true);
         }
-        // Use a random password to simulate connection failure.
-        if (TestUtils.isScanResultForWpa2Network(scanResult)) {
-            mNetworkSuggestionBuilder.setWpa2Passphrase(mTestUtils.generateRandomPassphrase());
-        } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
-            mNetworkSuggestionBuilder.setWpa3Passphrase(mTestUtils.generateRandomPassphrase());
+        if (mSimulateConnectionFailure) {
+            // Use a random password to simulate connection failure.
+            if (TestUtils.isScanResultForWpa2Network(scanResult)) {
+                mNetworkSuggestionBuilder.setWpa2Passphrase(mTestUtils.generateRandomPassphrase());
+            } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
+                mNetworkSuggestionBuilder.setWpa3Passphrase(mTestUtils.generateRandomPassphrase());
+            }
+        } else if (!mPsk.isEmpty()) {
+            if (TestUtils.isScanResultForWpa2Network(scanResult)) {
+                mNetworkSuggestionBuilder.setWpa2Passphrase(mPsk);
+            } else if (TestUtils.isScanResultForWpa3Network(scanResult)) {
+                mNetworkSuggestionBuilder.setWpa3Passphrase(mPsk);
+            }
         }
         return mNetworkSuggestionBuilder.build();
     }
@@ -172,10 +180,14 @@
 
     @Override
     protected boolean executeTest() throws InterruptedException {
-        // Step: Scan and find any open network around.
-        if (DBG) Log.v(TAG, "Scan and find a network");
+        if (mSimulateConnectionFailure && mPsk.isEmpty()) {
+            setFailureReason(mContext.getString(R.string.wifi_status_need_psk));
+            return false;
+        }
+        // Step: Scan and find the network around.
+        if (DBG) Log.v(TAG, "Scan and find the network: " + mSsid);
         ScanResult testNetwork = mTestUtils.startScanAndFindAnyMatchingNetworkInResults(
-                mSimulateConnectionFailure ? SCAN_RESULT_TYPE_PSK : SCAN_RESULT_TYPE_OPEN);
+                mSsid, mPsk.isEmpty() ? SCAN_RESULT_TYPE_OPEN : SCAN_RESULT_TYPE_PSK);
         if (testNetwork == null) {
             setFailureReason(mContext.getString(R.string.wifi_status_scan_failure));
             return false;
@@ -215,7 +227,7 @@
 
         // Step: Create a suggestion for the chosen open network depending on the type of test.
         WifiNetworkSuggestion networkSuggestion = createNetworkSuggestion(testNetwork);
-        mNetworkSuggestions.add(networkSuggestion);
+        mNetworkSuggestions = Arrays.asList(networkSuggestion);
 
         // Step: Add a network suggestions.
         if (DBG) Log.v(TAG, "Adding suggestion");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LocationModeSetter.java b/common/host-side/util-axt/src/com/android/compatibility/common/util/LocationModeSetter.java
similarity index 96%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LocationModeSetter.java
rename to common/host-side/util-axt/src/com/android/compatibility/common/util/LocationModeSetter.java
index f5999bb..bb4e673 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LocationModeSetter.java
+++ b/common/host-side/util-axt/src/com/android/compatibility/common/util/LocationModeSetter.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.devicepolicy;
+package com.android.compatibility.common.util;
 
 import com.android.tradefed.device.ITestDevice;
 
diff --git a/hostsidetests/appsecurity/Android.bp b/hostsidetests/appsecurity/Android.bp
index 3cc61fe..ae185c70 100644
--- a/hostsidetests/appsecurity/Android.bp
+++ b/hostsidetests/appsecurity/Android.bp
@@ -50,18 +50,8 @@
         "CtsCorruptApkTests_Unaligned_R",
     ],
 
-    data: [
-        ":CtsApkVerityTestApp",
-        ":CtsApkVerityTestAppFsvSig",
-        ":CtsApkVerityTestAppDm",
-        ":CtsApkVerityTestAppDmFsvSig",
-        ":CtsApkVerityTestAppSplit",
-        ":CtsApkVerityTestAppSplitFsvSig",
-        ":CtsApkVerityTestAppSplitDm",
-        ":CtsApkVerityTestAppSplitDmFsvSig",
-        ":CtsApkVerityTestApp2",
-        ":CtsApkVerityTestApp2FsvSig",
-    ],
+    // Prebuilts of all ABIs.
+    data: [":CtsApkVerityTestPrebuiltFiles"],
 }
 
 filegroup {
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-1mb-trailing-data.apk
similarity index 100%
rename from hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk
rename to hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-1mb-trailing-data.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-1mb-trailing-data.apk.idsig
similarity index 100%
rename from hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk.idsig
rename to hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-1mb-trailing-data.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-bit-flipped.apk
similarity index 100%
copy from hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-10mb-trailing-data.apk
copy to hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-bit-flipped.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-bit-flipped.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-bit-flipped.apk.idsig
new file mode 100644
index 0000000..a3fa2ab
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-merkle-tree-bit-flipped.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
index a21cee3..212986a 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
@@ -39,12 +39,12 @@
 
     private static final String PACKAGE_NAME = "android.appsecurity.cts.apkveritytestapp";
 
-    private static final String BASE_APK = "CtsApkVerityTestApp.apk";
-    private static final String BASE_APK_DM = "CtsApkVerityTestApp.dm";
-    private static final String SPLIT_APK = "CtsApkVerityTestAppSplit.apk";
-    private static final String SPLIT_APK_DM = "CtsApkVerityTestAppSplit.dm";
-    private static final String BAD_BASE_APK = "CtsApkVerityTestApp2.apk";
-    private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2.dm";
+    private static final String BASE_APK = "CtsApkVerityTestAppPrebuilt.apk";
+    private static final String BASE_APK_DM = "CtsApkVerityTestAppPrebuilt.dm";
+    private static final String SPLIT_APK = "CtsApkVerityTestAppSplitPrebuilt.apk";
+    private static final String SPLIT_APK_DM = "CtsApkVerityTestAppSplitPrebuilt.dm";
+    private static final String BAD_BASE_APK = "CtsApkVerityTestApp2Prebuilt.apk";
+    private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2Prebuilt.dm";
     private static final String FSV_SIG_SUFFIX = ".fsv_sig";
     private static final String APK_VERITY_STANDARD_MODE = "2";
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index cb170c9..54c00dc 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -64,7 +64,7 @@
 
     T addFile(String file) throws FileNotFoundException {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        mFiles.add(buildHelper.getTestFile(file));
+        mFiles.add(buildHelper.getTestFile(file, mAbi));
         return (T) this;
     }
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 3f54a05..fb31866 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -1088,10 +1088,21 @@
         }
 
         // Editing apksigner to add trailing data after the Merkle tree
-        assertInstallV4FailsWithError("v4-digest-v3-merkle-tree-10mb-trailing-data.apk",
+        assertInstallV4FailsWithError("v4-digest-v3-merkle-tree-1mb-trailing-data.apk",
                 "Failure");
     }
 
+    public void testInstallV4WithMerkleTreeBitsFlipped() throws Exception {
+        // V4 is only enabled on devices with Incremental feature
+        if (!hasIncrementalFeature()) {
+            return;
+        }
+
+        // Editing apksigner to flip few bits in the only node of the Merkle tree of a small app.
+        assertInstallV4FailsWithError("v4-digest-v3-merkle-tree-bit-flipped.apk",
+                "Failed to parse");
+    }
+
     public void testV4IncToV3NonIncSameKeyUpgradeSucceeds() throws Exception {
         // V4 is only enabled on devices with Incremental feature
         if (!hasIncrementalFeature()) {
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
index 3fc6f86..9dda405 100644
--- a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
@@ -12,6 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// A rule to collect apps for debugging purpose. See ApkVerityTestAppPrebuilt/README.md.
+genrule {
+    name: "CtsApkVerityTestDebugFiles",
+    srcs: [
+        ":CtsApkVerityTestApp",
+        ":CtsApkVerityTestAppFsvSig",
+        ":CtsApkVerityTestAppDm",
+        ":CtsApkVerityTestAppDmFsvSig",
+        ":CtsApkVerityTestAppSplit",
+        ":CtsApkVerityTestAppSplitFsvSig",
+        ":CtsApkVerityTestAppSplitDm",
+        ":CtsApkVerityTestAppSplitDmFsvSig",
+    ],
+    cmd: "echo $(in) > $(out)",
+    out: ["CtsApkVerityTestDebugFiles.txt"],
+}
+
 filegroup {
     name: "CtsApkVerityTestAppDm",
     srcs: ["CtsApkVerityTestApp.dm"],
@@ -72,18 +89,3 @@
     srcs: [":CtsApkVerityTestAppSplitDm"],
     out: ["CtsApkVerityTestAppSplit.dm.fsv_sig"],
 }
-
-// Create another app with mismatched .fsv_sig
-genrule {
-    name: "CtsApkVerityTestApp2",
-    srcs: [":CtsApkVerityTestApp"],
-    out: ["CtsApkVerityTestApp2.apk"],
-    cmd: "cp -f $(in) $(out)"
-}
-
-genrule {
-    name: "CtsApkVerityTestApp2FsvSig",
-    srcs: [":CtsApkVerityTestAppSplitFsvSig"],
-    out: ["CtsApkVerityTestApp2.apk.fsv_sig"],
-    cmd: "cp -f $(in) $(out)"
-}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/Android.bp
new file mode 100644
index 0000000..cf37807
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 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.
+
+// Prebuilts that are signed with corresponding key of
+// build/make/target/product/security/fsverity-release.x509.der
+
+filegroup {
+    name: "CtsApkVerityTestPrebuiltFiles",
+    srcs: [
+        "arm/*",
+        "arm64/*",
+        "x86/*",
+        "x86_64/*",
+    ],
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
index 968675c..6e6e408 100644
--- a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
@@ -25,4 +25,60 @@
 
 How to use the app built locally
 --------------------------------
-TODO: provide instruction once the test switches to prebuilt and release signature.
+You need to override the prebuilts with the debug build.
+
+1. Build the debug artifacts by `m CtsApkVerityTestDebugFiles`. Copy the output
+   to a temporary directory, e.g.
+
+```
+(cd $ANDROID_BUILD_TOP && cp `cat
+out/soong/.intermediates/cts/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestDebugFiles/gen/CtsApkVerityTestDebugFiles.txt`
+/tmp/prebuilts/)
+```
+
+2. Copy files to create bad app, e.g. in /tmp/prebuilts,
+
+```
+cp CtsApkVerityTestApp.apk CtsApkVerityTestApp2.apk
+cp CtsApkVerityTestAppSplit.apk.fsv_sig CtsApkVerityTestApp2.apk.fsv_sig
+```
+
+3. Rename file names to match the test expectation.
+```
+for f in CtsApkVerityTestApp*; do echo $f | sed -E 's/([^.]+)\.(.+)/mv & \1Prebuilt.\2/'; done | sh
+```
+
+4. Run the test.
+
+```
+atest CtsAppSecurityHostTestCases:android.appsecurity.cts.ApkVerityInstallTest
+```
+
+How to update the prebuilts
+===========================
+
+1. Download android-cts.zip. The current prebuilts are downloaded from the links below.
+   TODO(157658439): update the links once we have the correct build target.
+
+```
+https://android-build.googleplex.com/builds/submitted/6472922/test_suites_arm64/latest/android-cts.zip
+https://android-build.googleplex.com/builds/submitted/6472922/test_suites_x86_64/latest/android-cts.zip
+```
+
+2. Extract CtsApkVerityTestApp\*.{apk,dm} and ask the key owner to sign
+   (example: b/152753442).
+3. Receive the release signature .fsv\_sig.
+4. Override CtsApkVerityTestApp2 to create a bad signature.
+
+```
+cp CtsApkVerityTestApp.apk CtsApkVerityTestApp2.apk
+cp CtsApkVerityTestAppSplit.apk.fsv_sig CtsApkVerityTestApp2.apk.fsv_sig
+```
+
+5. Rename to "Prebuilt".
+
+```
+for f in CtsApkVerityTestApp*; do echo $f | sed -E 's/([^.]+)\.(.+)/mv & \1Prebuilt.\2/'; done | sh
+```
+
+6. Duplicate arm64 prebuilts into arm and arm64, x86\_64 into x86 and x86\_64.
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk
new file mode 100644
index 0000000..55542bb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk
new file mode 100644
index 0000000..55542bb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..db8b3ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..eab5745
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk
new file mode 100644
index 0000000..49265ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..b39c3b5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk
new file mode 100644
index 0000000..55542bb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk
new file mode 100644
index 0000000..55542bb
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..db8b3ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..eab5745
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk
new file mode 100644
index 0000000..49265ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..b39c3b5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/arm64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk
new file mode 100644
index 0000000..6d08ba5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk
new file mode 100644
index 0000000..6d08ba5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..285e58b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..eab5745
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk
new file mode 100644
index 0000000..49265ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..b39c3b5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk
new file mode 100644
index 0000000..6d08ba5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestApp2Prebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk
new file mode 100644
index 0000000..6d08ba5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..285e58b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..eab5745
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk
new file mode 100644
index 0000000..49265ff
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
new file mode 100644
index 0000000..8554714
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.apk.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
new file mode 100644
index 0000000..b39c3b5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/x86_64/CtsApkVerityTestAppSplitPrebuilt.dm.fsv_sig
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index 13818b7..6a7aa7e 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -16,9 +16,6 @@
 
 package com.android.cts.writeexternalstorageapp;
 
-import static android.test.MoreAsserts.assertNotEqual;
-
-import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.TAG;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoWriteAccess;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadOnlyAccess;
@@ -34,11 +31,10 @@
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
 
 import android.os.Environment;
-import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
 import android.provider.MediaStore;
 import android.system.Os;
 import android.test.AndroidTestCase;
-import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.cts.externalstorageapp.CommonExternalStorageTest;
@@ -90,15 +86,25 @@
     }
 
     public void testWriteExternalStorage() throws Exception {
-        final long testValue = 12345000;
+        final long newTimeMillis = 12345000;
         assertExternalStorageMounted();
 
         // Write a value and make sure we can read it back
         writeInt(TEST_FILE, 32);
         assertEquals(readInt(TEST_FILE), 32);
 
-        assertTrue("Must be able to set last modified", TEST_FILE.setLastModified(testValue));
-        assertEquals(testValue, TEST_FILE.lastModified());
+        assertTrue("Must be able to set last modified", TEST_FILE.setLastModified(newTimeMillis));
+
+        // This uses the same fd, so info is cached by VFS.
+        assertEquals(newTimeMillis, TEST_FILE.lastModified());
+
+        // Obtain a new fd, using the low FS and check timestamp on it.
+        ParcelFileDescriptor fd =
+                getContext().getContentResolver().openFileDescriptor(
+                        MediaStore.scanFile(getContext().getContentResolver(), TEST_FILE), "rw");
+
+        long newTimeSeconds = newTimeMillis / 1000;
+        assertEquals(newTimeSeconds, Os.fstat(fd.getFileDescriptor()).st_mtime);
     }
 
     public void testWriteExternalStorageDirs() throws Exception {
diff --git a/hostsidetests/content/test-apps/ContextCrossProfileApps/ContextCrossProfileApp/AndroidManifest.xml b/hostsidetests/content/test-apps/ContextCrossProfileApps/ContextCrossProfileApp/AndroidManifest.xml
index 54deebf..a129819 100644
--- a/hostsidetests/content/test-apps/ContextCrossProfileApps/ContextCrossProfileApp/AndroidManifest.xml
+++ b/hostsidetests/content/test-apps/ContextCrossProfileApps/ContextCrossProfileApp/AndroidManifest.xml
@@ -16,6 +16,9 @@
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.cts.context">
+
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+
     <application>
         <uses-library android:name="android.test.runner" />
         <service
diff --git a/hostsidetests/devicepolicy/Android.bp b/hostsidetests/devicepolicy/Android.bp
index beba9d4..0e89a22 100644
--- a/hostsidetests/devicepolicy/Android.bp
+++ b/hostsidetests/devicepolicy/Android.bp
@@ -21,6 +21,7 @@
         "cts-tradefed",
         "tradefed",
         "compatibility-host-util",
+	"compatibility-host-util-axt",
         "guava",
         "truth-prebuilt",
     ],
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
index 041900c..409b076 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import java.time.Duration;
 
@@ -90,14 +89,14 @@
     /**
      * 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.
+     *
+     * <p>Does not test that the locked activity is initially in the foreground, since running this
+     * test in instrumentation can immediately kill the locked activity (while maintaining lock task
+     * mode).
      */
     public void testLockTaskIsActiveAndCantBeInterrupted() throws Exception {
         Log.d(TAG, "testLockTaskIsActiveAndCantBeInterrupted on host-driven test");
-        waitAndCheckLockedActivityIsResumed();
-        checkLockedActivityIsRunning();
-
-        mUiDevice.pressBack();
-        mUiDevice.waitForIdle();
+        waitAndEnsureLockTaskUtilityActivityIsRunning();
         checkLockedActivityIsRunning();
 
         mUiDevice.pressHome();
@@ -108,6 +107,10 @@
         mUiDevice.waitForIdle();
         checkLockedActivityIsRunning();
 
+        mUiDevice.pressBack();
+        mUiDevice.waitForIdle();
+        checkLockedActivityIsRunning();
+
         mUiDevice.waitForIdle();
     }
 
@@ -230,6 +233,19 @@
                 ActivityManager.LOCK_TASK_MODE_LOCKED, mActivityManager.getLockTaskModeState());
     }
 
+    /**
+     * Ensures the locked activity is resumed or otherwise launches it but without starting lock
+     * task if it is not already in that mode.
+     */
+    private void waitAndEnsureLockTaskUtilityActivityIsRunning() throws Exception {
+        mUiDevice.waitForIdle();
+        final boolean lockedActivityIsResumed =
+                LockTaskUtilityActivity.waitUntilActivityResumed(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+        if (!lockedActivityIsResumed) {
+            launchLockTaskUtilityActivityWithoutStartingLockTask();
+        }
+    }
+
     private void waitAndCheckLockedActivityIsResumed() throws Exception {
         mUiDevice.waitForIdle();
         assertTrue(
@@ -269,6 +285,12 @@
         mContext.startActivity(intent);
     }
 
+    private void launchLockTaskUtilityActivityWithoutStartingLockTask() {
+        final Intent intent = new Intent(mContext, LockTaskUtilityActivityIfWhitelisted.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
     private void setLockTaskPackages(String... packages) {
         mDevicePolicyManager.setLockTaskPackages(ADMIN_RECEIVER_COMPONENT, packages);
     }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RandomizedWifiMacAddressTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RandomizedWifiMacAddressTest.java
deleted file mode 100644
index f9db3e5..0000000
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RandomizedWifiMacAddressTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.google.common.truth.Truth.assertWithMessage;
-
-import android.net.MacAddress;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-
-import com.android.compatibility.common.util.WifiConfigCreator;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Tests that DO/PO can access randomized WiFi addresses.
- */
-public class RandomizedWifiMacAddressTest extends BaseDeviceAdminTest {
-    /** Mac address returned when the caller doesn't have access. */
-    private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
-    /** SSID returned when the caller doesn't have access or if WiFi is not connected. */
-    private static final String NETWORK_SSID = "TestSSID";
-    private static final String DEFAULT_SSID = "<unknown ssid>";
-
-    private int mNetId;
-    private WifiConfigCreator mWifiConfigCreator;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mWifiConfigCreator = new WifiConfigCreator(mContext);
-        mNetId = mWifiConfigCreator.addNetwork(NETWORK_SSID, false,
-                WifiConfigCreator.SECURITY_TYPE_NONE, null);
-        assertWithMessage("Fail to create test network")
-                .that(mNetId)
-                .isNotEqualTo(-1);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mWifiConfigCreator.removeNetwork(mNetId);
-        super.tearDown();
-    }
-    public void testGetRandomizedMacAddress() {
-        final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
-
-        final List<WifiConfiguration> wifiConfigs = wifiManager.getConfiguredNetworks();
-        for (final WifiConfiguration config : wifiConfigs) {
-            if (config.SSID == null) {
-                continue;
-            }
-
-            if (config.SSID.equals("\"" + NETWORK_SSID + "\"")) {
-                final MacAddress macAddress = config.getRandomizedMacAddress();
-
-                assertWithMessage("Device owner should be able to get the randomized MAC address")
-                        .that(macAddress)
-                        .isNotEqualTo((MacAddress.fromString(DEFAULT_MAC_ADDRESS)));
-                return;
-            }
-        }
-
-        final String ssids = wifiConfigs.stream()
-                .map(c -> c.SSID).filter(Objects::nonNull).collect(Collectors.joining(","));
-
-        fail(String.format("Failed to find WifiConfiguration for the current connection, " +
-                "current SSID: %s; configured SSIDs: %s", NETWORK_SSID, ssids));
-    }
-}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileSharingTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileSharingTest.java
new file mode 100644
index 0000000..65261e1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileSharingTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 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.managedprofile;
+
+import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+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.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * App-side tests for cross-profile sharing
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileSharingTest {
+
+    @Test
+    public void startSwitchToOtherProfileIntent() {
+        Intent intent = getSendIntent();
+        Context context = InstrumentationRegistry.getContext();
+        List<ResolveInfo> resolveInfos =
+                context.getPackageManager().queryIntentActivities(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY);
+        ResolveInfo switchToOtherProfileResolveInfo =
+                getSwitchToOtherProfileResolveInfo(resolveInfos);
+        assertWithMessage("Could not retrieve the switch to other profile resolve info.")
+                .that(switchToOtherProfileResolveInfo)
+                .isNotNull();
+        ActivityInfo activityInfo = switchToOtherProfileResolveInfo.activityInfo;
+        ComponentName componentName =
+                new ComponentName(activityInfo.packageName, activityInfo.name);
+        Intent switchToOtherProfileIntent = new Intent(intent);
+        switchToOtherProfileIntent.setComponent(componentName);
+        switchToOtherProfileIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(switchToOtherProfileIntent);
+    }
+
+    @Test
+    public void startSwitchToOtherProfileIntent_chooser() {
+        Intent intent = getSendIntent();
+        Context context = InstrumentationRegistry.getContext();
+        List<ResolveInfo> resolveInfos =
+                context.getPackageManager().queryIntentActivities(intent,
+                        PackageManager.MATCH_DEFAULT_ONLY);
+        ResolveInfo switchToOtherProfileResolveInfo =
+                getSwitchToOtherProfileResolveInfo(resolveInfos);
+        assertWithMessage("Could not retrieve the switch to other profile resolve info.")
+                .that(switchToOtherProfileResolveInfo)
+                .isNotNull();
+        ActivityInfo activityInfo = switchToOtherProfileResolveInfo.activityInfo;
+        ComponentName componentName =
+                new ComponentName(activityInfo.packageName, activityInfo.name);
+        Intent chooserIntent = Intent.createChooser(intent, /* title */ null);
+        Intent switchToOtherProfileIntent = new Intent(chooserIntent);
+        switchToOtherProfileIntent.setComponent(componentName);
+        switchToOtherProfileIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(switchToOtherProfileIntent);
+    }
+
+    @Test
+    public void addCrossProfileIntents()
+            throws IntentFilter.MalformedMimeTypeException {
+        Context context = InstrumentationRegistry.getContext();
+        DevicePolicyManager devicePolicyManager =
+                context.getSystemService(DevicePolicyManager.class);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SEND);
+        filter.addCategory(Intent.CATEGORY_DEFAULT);
+        filter.addDataType("*/*");
+        devicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, filter,
+                DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT |
+                DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+    }
+
+    @Test
+    public void clearCrossProfileIntents() {
+        Context context = InstrumentationRegistry.getContext();
+        DevicePolicyManager devicePolicyManager =
+                context.getSystemService(DevicePolicyManager.class);
+        devicePolicyManager.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
+    }
+
+    private Intent getSendIntent() {
+        Intent intent = new Intent(Intent.ACTION_SEND);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.putExtra(Intent.EXTRA_TEXT, "test example");
+        intent.setType("text/plain");
+        return intent;
+    }
+
+    private ResolveInfo getSwitchToOtherProfileResolveInfo(List<ResolveInfo> resolveInfos) {
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            // match == 0 means that this intent actually doesn't match to anything on this profile,
+            // meaning it should be on the other side.
+            if (resolveInfo.match == 0) {
+                return resolveInfo;
+            }
+        }
+        return null;
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/SharingApps/Android.bp b/hostsidetests/devicepolicy/app/SharingApps/Android.bp
new file mode 100644
index 0000000..460fe55
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 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: "SharingApp1",
+    defaults: ["cts_defaults"],
+    platform_apis: true,
+    srcs: ["sharingapp1/src/**/*.java"],
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ub-uiautomator",
+        "cts-security-test-support-library",
+        "androidx.legacy_legacy-support-v4",
+    ],
+    min_sdk_version: "23",
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    manifest: "sharingapp1/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "SharingApp2",
+    defaults: ["cts_defaults"],
+    platform_apis: true,
+    srcs: ["sharingapp2/src/**/*.java"],
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ub-uiautomator",
+        "cts-security-test-support-library",
+        "androidx.legacy_legacy-support-v4",
+    ],
+    min_sdk_version: "23",
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    manifest: "sharingapp2/AndroidManifest.xml",
+}
diff --git a/hostsidetests/devicepolicy/app/SharingApps/OWNERS b/hostsidetests/devicepolicy/app/SharingApps/OWNERS
new file mode 100644
index 0000000..2638902
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 168445
+alexkershaw@google.com
+arangelov@google.com
+scottjonathan@google.com
+kholoudm@google.com
diff --git a/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/AndroidManifest.xml
new file mode 100644
index 0000000..e6c7f42
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<!--
+  ~ A dummy app used for when you need to install test packages that have a functioning package name
+  ~ and UID. For example, you could use it to set permissions or app-ops.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.sharingapps.sharingapp1">
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".SimpleActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/src/SimpleActivity.java b/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/src/SimpleActivity.java
new file mode 100644
index 0000000..93fb386
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/sharingapp1/src/SimpleActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.sharingapps.sharingapp1;
+
+import android.app.Activity;
+
+/**
+ * A simple activity to install for various users to test the intent resolver.
+ */
+public class SimpleActivity extends Activity {
+
+}
diff --git a/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/AndroidManifest.xml
new file mode 100644
index 0000000..dbd3be3
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<!--
+  ~ A dummy app used for when you need to install test packages that have a functioning package name
+  ~ and UID. For example, you could use it to set permissions or app-ops.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.sharingapps.sharingapp2">
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".SimpleActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/src/SimpleActivity.java b/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/src/SimpleActivity.java
new file mode 100644
index 0000000..3e17cec
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SharingApps/sharingapp2/src/SimpleActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.sharingapps.sharingapp2;
+
+import android.app.Activity;
+
+/**
+ * A simple activity to install for various users to test the intent resolver.
+ */
+public class SimpleActivity extends Activity {
+
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
index f9cf486..b59d82f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
@@ -38,6 +38,8 @@
     protected static final String DUMMY_APP_2_APK = "DummyApp2.apk";
     protected static final String DUMMY_APP_3_APK = "DummyApp3.apk";
     protected static final String DUMMY_APP_4_APK = "DummyApp4.apk";
+    protected static final String SHARING_APP_1_APK = "SharingApp1.apk";
+    protected static final String SHARING_APP_2_APK = "SharingApp2.apk";
     private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
     private static final String NOTIFICATION_PKG =
             "com.android.cts.managedprofiletests.notificationsender";
@@ -83,6 +85,8 @@
             getDevice().uninstallPackage(DUMMY_APP_2_APK);
             getDevice().uninstallPackage(DUMMY_APP_3_APK);
             getDevice().uninstallPackage(DUMMY_APP_4_APK);
+            getDevice().uninstallPackage(SHARING_APP_1_APK);
+            getDevice().uninstallPackage(SHARING_APP_2_APK);
         }
         super.tearDown();
     }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 07e0ace..d828b86 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -28,6 +28,7 @@
 import android.platform.test.annotations.RequiresDevice;
 import android.stats.devicepolicy.EventId;
 
+import com.android.compatibility.common.util.LocationModeSetter;
 import com.android.cts.devicepolicy.annotations.LockSettingsTest;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -2048,17 +2049,6 @@
     }
 
     @Test
-    public void testRandomizedWifiMacAddress() throws Exception {
-        if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
-            return;
-        }
-        try (LocationModeSetter locationModeSetter = new LocationModeSetter(getDevice())) {
-            locationModeSetter.setLocationEnabled(true);
-            executeDeviceTestClass(".RandomizedWifiMacAddressTest");
-        }
-    }
-
-    @Test
     public void testIsDeviceOrganizationOwnedWithManagedProfile() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 93cc6eb..b428d788 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -29,6 +29,7 @@
 import android.stats.devicepolicy.EventId;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.LocationModeSetter;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.tradefed.device.DeviceNotAvailableException;
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 639c6a3..90689bc 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -507,10 +507,7 @@
             return;
         }
         installAllDummyApps();
-        getDevice().clearLogcat();
-        // Increase logcat size because the test is reading from it.
-        String command = "logcat -G 16M";
-        getDevice().executeShellCommand(command);
+        setupLogcatForTest();
 
         runWorkProfileDeviceTest(
                 ".CrossProfileTest",
@@ -523,6 +520,14 @@
                 MAINTAINED_CROSS_PROFILE_PACKAGES);
     }
 
+    private void setupLogcatForTest() throws Exception {
+        // Clear and increase logcat buffer size because the test is reading from it.
+        final String clearLogcatCommand = "logcat -c";
+        getDevice().executeShellCommand(clearLogcatCommand);
+        final String increaseLogcatBufferCommand = "logcat -G 16M";
+        getDevice().executeShellCommand(increaseLogcatBufferCommand);
+    }
+
     /** Assumes that logcat is clear before running the test. */
     private void assertDummyAppsReceivedCanInteractAcrossProfilesChangedBroadcast(
             Set<String> packageNames)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index e452368..f5726d3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -18,6 +18,8 @@
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -26,11 +28,13 @@
 import android.platform.test.annotations.LargeTest;
 import android.stats.devicepolicy.EventId;
 
+import com.android.compatibility.common.util.LocationModeSetter;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 
+
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -707,6 +711,119 @@
                 "testCreateProfile_managedProfile", mPrimaryUserId);
     }
 
+    @Test
+    public void testResolverActivityLaunchedFromPersonalProfileWithSelectedWorkTab()
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(SHARING_APP_1_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_2_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_1_APK, mProfileUserId);
+        installAppAsUser(SHARING_APP_2_APK, mProfileUserId);
+        try {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "addCrossProfileIntents", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "startSwitchToOtherProfileIntent", mPrimaryUserId);
+            assertResolverActivityInForeground(mPrimaryUserId);
+        } finally {
+            pressHome();
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "clearCrossProfileIntents", mProfileUserId);
+        }
+    }
+
+    @Test
+    public void testResolverActivityLaunchedFromWorkProfileWithSelectedPersonalTab()
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(SHARING_APP_1_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_2_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_1_APK, mProfileUserId);
+        installAppAsUser(SHARING_APP_2_APK, mProfileUserId);
+        try {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "addCrossProfileIntents", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "startSwitchToOtherProfileIntent", mProfileUserId);
+            assertResolverActivityInForeground(mProfileUserId);
+        } finally {
+            pressHome();
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "clearCrossProfileIntents", mProfileUserId);
+        }
+    }
+
+    @Test
+    public void testChooserActivityLaunchedFromPersonalProfileWithSelectedWorkTab()
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(SHARING_APP_1_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_2_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_1_APK, mProfileUserId);
+        installAppAsUser(SHARING_APP_2_APK, mProfileUserId);
+        try {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "addCrossProfileIntents", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "startSwitchToOtherProfileIntent_chooser", mPrimaryUserId);
+            assertChooserActivityInForeground(mPrimaryUserId);
+        } finally {
+            pressHome();
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "clearCrossProfileIntents", mProfileUserId);
+        }
+    }
+
+    @Test
+    public void testChooserActivityLaunchedFromWorkProfileWithSelectedPersonalTab()
+            throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(SHARING_APP_1_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_2_APK, mPrimaryUserId);
+        installAppAsUser(SHARING_APP_1_APK, mProfileUserId);
+        installAppAsUser(SHARING_APP_2_APK, mProfileUserId);
+        try {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "addCrossProfileIntents", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "startSwitchToOtherProfileIntent_chooser", mProfileUserId);
+            assertChooserActivityInForeground(mProfileUserId);
+        } finally {
+            pressHome();
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileSharingTest",
+                    "clearCrossProfileIntents", mProfileUserId);
+        }
+    }
+
+    private void pressHome() throws Exception {
+        executeShellCommand("input keyevent KEYCODE_HOME");
+    }
+
+    private void assertChooserActivityInForeground(int userId)
+            throws DeviceNotAvailableException {
+        assertActivityInForeground("android/com.android.internal.app.ChooserActivity", userId);
+    }
+
+    private void assertResolverActivityInForeground(int userId)
+            throws DeviceNotAvailableException {
+        assertActivityInForeground("android/com.android.internal.app.ResolverActivity", userId);
+    }
+
+    private void assertActivityInForeground(String fullActivityName, int userId)
+            throws DeviceNotAvailableException {
+        String commandOutput =
+                getDevice().executeShellCommand("dumpsys activity activities | grep Resumed:");
+        assertThat(commandOutput).contains("u" + userId + " " + fullActivityName);
+    }
+
     private void changeUserRestrictionOrFail(String key, boolean value, int userId)
             throws DeviceNotAvailableException {
         changeUserRestrictionOrFail(key, value, userId, MANAGED_PROFILE_PKG);
diff --git a/hostsidetests/incident/OWNERS b/hostsidetests/incident/OWNERS
index c8e4578..37c0932 100644
--- a/hostsidetests/incident/OWNERS
+++ b/hostsidetests/incident/OWNERS
@@ -1,7 +1,13 @@
 # Bug component: 329246
+jeffreyhuang@google.com
 joeo@google.com
 jreck@google.com
 kwekua@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
 yamasani@google.com
 yanmin@google.com
+yro@google.com
 zhouwenjie@google.com
diff --git a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
index 843531d..1e0a215 100644
--- a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
@@ -101,7 +101,7 @@
         // Although our current stats don't distinguish between ANIMATION, LAYOUT, and RECORD_DRAW
         // so this will just be slowUi +30
         int slowUiDelta = summaryAfter.getSlowUiThreadCount() - summaryBefore.getSlowUiThreadCount();
-        assertThat(slowUiDelta).isAtLeast(28);
+        assertThat(slowUiDelta).isAtLeast(20);
         int missedVsyncDelta = summaryAfter.getMissedVsyncCount()
                 - summaryBefore.getMissedVsyncCount();
         assertThat(missedVsyncDelta).isIn(Range.closed(10, 11));
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index ec884d0..f3cd8a9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -130,14 +130,11 @@
             setLastCallback(CallbackState.CAPABILITIES, network, cap);
         }
 
-        public void expectLostCallback(Network expectedNetwork) {
-            expectCallback(CallbackState.LOST, expectedNetwork, null);
-        }
-
         public Network expectAvailableCallbackAndGetNetwork() {
             final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
             if (cb.state != CallbackState.AVAILABLE) {
-                fail("Network is not available");
+                fail("Network is not available. Instead obtained the following callback :"
+                        + cb);
             }
             return cb.network;
         }
@@ -152,7 +149,7 @@
             do {
                 final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
                 if (cb.state == CallbackState.BLOCKED_STATUS) {
-                    assertEquals(expectBlocked, (Boolean) cb.arg);
+                    assertEquals(expectBlocked, cb.arg);
                     return;
                 }
             } while (System.currentTimeMillis() <= deadline);
@@ -165,10 +162,10 @@
             final NetworkCapabilities cap = (NetworkCapabilities) cb.arg;
             assertEquals(expectedNetwork, cb.network);
             assertEquals(CallbackState.CAPABILITIES, cb.state);
-            if (hasCapability) {
-                assertTrue(cap.hasCapability(capability));
-            } else {
-                assertFalse(cap.hasCapability(capability));
+            if (hasCapability != cap.hasCapability(capability)) {
+                fail("NetworkCapabilities callback "
+                        + (hasCapability ? "missing expected" : "has unexpected")
+                        + " capability. " + cb);
             }
         }
     }
diff --git a/hostsidetests/packagemanager/extractnativelibs/Android.bp b/hostsidetests/packagemanager/extractnativelibs/Android.bp
index 93f55a1..34c4a4d 100644
--- a/hostsidetests/packagemanager/extractnativelibs/Android.bp
+++ b/hostsidetests/packagemanager/extractnativelibs/Android.bp
@@ -27,4 +27,5 @@
         "tradefed",
         "compatibility-host-util",
     ],
+    java_resource_dirs: ["res"],
 }
diff --git a/hostsidetests/packagemanager/extractnativelibs/apps/Android.bp b/hostsidetests/packagemanager/extractnativelibs/apps/Android.bp
index 4a27ad2..8d1a673 100644
--- a/hostsidetests/packagemanager/extractnativelibs/apps/Android.bp
+++ b/hostsidetests/packagemanager/extractnativelibs/apps/Android.bp
@@ -41,6 +41,7 @@
     static_libs: ["androidx.test.rules"],
     use_embedded_native_libs: true,
     compile_multilib: "both",
+    v4_signature: true,
 }
 
 android_test_helper_app {
@@ -60,5 +61,6 @@
     static_libs: ["androidx.test.rules"],
     use_embedded_native_libs: false,
     compile_multilib: "both",
+    v4_signature: true,
 }
 
diff --git a/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk b/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk
new file mode 100644
index 0000000..f5aed6d
--- /dev/null
+++ b/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk
Binary files differ
diff --git a/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk.idsig b/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk.idsig
new file mode 100644
index 0000000..c67b2bd
--- /dev/null
+++ b/hostsidetests/packagemanager/extractnativelibs/res/prebuilt/CtsExtractNativeLibsAppFalseWithMisalignedLib.apk.idsig
Binary files differ
diff --git a/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTest.java b/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTest.java
index 6bfe2f2..11ae846 100644
--- a/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTest.java
+++ b/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTest.java
@@ -15,22 +15,38 @@
  */
 package android.extractnativelibs.cts;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.platform.test.annotations.AppModeFull;
 
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.FileUtil;
 
 import org.junit.After;
-import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
 /**
  * Host test to install test apps and run device tests to verify the effect of extractNativeLibs.
  * TODO(b/147496159): add more tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class CtsExtractNativeLibsHostTest extends BaseHostJUnit4Test {
+    private static final String TEST_REMOTE_DIR = "/data/local/tmp/extract_native_libs_test";
+    private static final String TEST_APK_RESOURCE_PREFIX = "/prebuilt/";
+    private static final String TEST_HOST_TMP_DIR_PREFIX = "cts_extract_native_libs_host_test";
+
     private static final String TEST_NO_EXTRACT_PKG =
             "com.android.cts.extractnativelibs.app.noextract";
     private static final String TEST_NO_EXTRACT_CLASS =
@@ -44,31 +60,125 @@
             TEST_EXTRACT_PKG + ".ExtractNativeLibsTrueDeviceTest";
     private static final String TEST_EXTRACT_TEST = "testNativeLibsExtracted";
     private static final String TEST_EXTRACT_APK = "CtsExtractNativeLibsAppTrue.apk";
+    private static final String TEST_NO_EXTRACT_MISALIGNED_APK =
+            "CtsExtractNativeLibsAppFalseWithMisalignedLib.apk";
 
+    private static final String IDSIG_SUFFIX = ".idsig";
+
+    /** Setup test dir. */
+    @Before
+    public void setUp() throws Exception {
+        getDevice().executeShellCommand("mkdir " + TEST_REMOTE_DIR);
+    }
     /** Uninstall apps after tests. */
     @After
     public void cleanUp() throws Exception {
         uninstallPackage(getDevice(), TEST_NO_EXTRACT_PKG);
         uninstallPackage(getDevice(), TEST_EXTRACT_PKG);
+        getDevice().executeShellCommand("rm -r " + TEST_REMOTE_DIR);
     }
 
     /** Test with a app that has extractNativeLibs=false. */
     @Test
     @AppModeFull
-    public void testNoExtractNativeLibs() throws Exception {
+    public void testNoExtractNativeLibsLegacy() throws Exception {
         installPackage(TEST_NO_EXTRACT_APK);
-        Assert.assertTrue(isPackageInstalled(TEST_NO_EXTRACT_PKG));
-        Assert.assertTrue(runDeviceTests(
+        assertTrue(isPackageInstalled(TEST_NO_EXTRACT_PKG));
+        assertTrue(runDeviceTests(
                 TEST_NO_EXTRACT_PKG, TEST_NO_EXTRACT_CLASS, TEST_NO_EXTRACT_TEST));
     }
 
     /** Test with a app that has extractNativeLibs=true. */
     @Test
     @AppModeFull
-    public void testExtractNativeLibs() throws Exception {
+    public void testExtractNativeLibsLegacy() throws Exception {
         installPackage(TEST_EXTRACT_APK);
-        Assert.assertTrue(isPackageInstalled(TEST_EXTRACT_PKG));
-        Assert.assertTrue(runDeviceTests(
+        assertTrue(isPackageInstalled(TEST_EXTRACT_PKG));
+        assertTrue(runDeviceTests(
                 TEST_EXTRACT_PKG, TEST_EXTRACT_CLASS, TEST_EXTRACT_TEST));
     }
+
+    /** Test with a app that has extractNativeLibs=false but with mis-aligned lib files */
+    @Test
+    @AppModeFull
+    public void testNoExtractNativeLibsFails() throws Exception {
+        File apk = getFileFromResource(TEST_NO_EXTRACT_MISALIGNED_APK);
+        String result = getDevice().installPackage(apk, false, true, "");
+        assertTrue(result.contains("Failed to extract native libraries"));
+        assertFalse(isPackageInstalled(TEST_NO_EXTRACT_PKG));
+    }
+
+    /** Test with a app that has extractNativeLibs=false using Incremental install. */
+    @Test
+    @AppModeFull
+    public void testNoExtractNativeLibsIncremental() throws Exception {
+        installPackageIncremental(TEST_NO_EXTRACT_APK);
+        assertTrue(isPackageInstalled(TEST_NO_EXTRACT_PKG));
+        assertTrue(runDeviceTests(
+                TEST_NO_EXTRACT_PKG, TEST_NO_EXTRACT_CLASS, TEST_NO_EXTRACT_TEST));
+    }
+
+    /** Test with a app that has extractNativeLibs=true using Incremental install. */
+    @Test
+    @AppModeFull
+    public void testExtractNativeLibsIncremental() throws Exception {
+        installPackageIncremental(TEST_EXTRACT_APK);
+        assertTrue(isPackageInstalled(TEST_EXTRACT_PKG));
+        assertTrue(runDeviceTests(
+                TEST_EXTRACT_PKG, TEST_EXTRACT_CLASS, TEST_EXTRACT_TEST));
+    }
+
+    /** Test with a app that has extractNativeLibs=false but with mis-aligned lib files,
+     *  using Incremental install. */
+    @Test
+    @AppModeFull
+    public void testExtractNativeLibsIncrementalFails() throws Exception {
+        String result = installIncrementalPackageFromResource(TEST_NO_EXTRACT_MISALIGNED_APK);
+        assertTrue(result.contains("Failed to extract native libraries"));
+        assertFalse(isPackageInstalled(TEST_NO_EXTRACT_PKG));
+    }
+
+    private String installPackageIncremental(String apkName) throws Exception {
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+        final File apk = buildHelper.getTestFile(apkName);
+        assertNotNull(apk);
+        final File v4Signature = buildHelper.getTestFile(apkName + IDSIG_SUFFIX);
+        assertNotNull(v4Signature);
+        return installPackageIncrementalFromFiles(apk, v4Signature);
+    }
+
+    private String installPackageIncrementalFromFiles(File apk, File v4Signature) throws Exception {
+        final String remoteApkPath = TEST_REMOTE_DIR + "/" + apk.getName();
+        final String remoteIdsigPath = remoteApkPath + IDSIG_SUFFIX;
+        assertTrue(getDevice().pushFile(apk, remoteApkPath));
+        assertTrue(getDevice().pushFile(v4Signature, remoteIdsigPath));
+        return getDevice().executeShellCommand("pm install-incremental -t -g " + remoteApkPath);
+    }
+
+    private String installIncrementalPackageFromResource(String apkFilenameInRes)
+            throws Exception {
+        final File apkFile = getFileFromResource(apkFilenameInRes);
+        final File v4SignatureFile = getFileFromResource(
+                apkFilenameInRes + IDSIG_SUFFIX);
+        return installPackageIncrementalFromFiles(apkFile, v4SignatureFile);
+    }
+
+    private File getFileFromResource(String filenameInResources)
+            throws Exception {
+        String fullResourceName = TEST_APK_RESOURCE_PREFIX + filenameInResources;
+        File tempDir = FileUtil.createTempDir(TEST_HOST_TMP_DIR_PREFIX);
+        File file = new File(tempDir, filenameInResources);
+        InputStream in = getClass().getResourceAsStream(fullResourceName);
+        if (in == null) {
+            throw new IllegalArgumentException("Resource not found: " + fullResourceName);
+        }
+        OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
+        byte[] buf = new byte[65536];
+        int chunkSize;
+        while ((chunkSize = in.read(buf)) != -1) {
+            out.write(buf, 0, chunkSize);
+        }
+        out.close();
+        return file;
+    }
 }
diff --git a/hostsidetests/scopedstorage/TEST_MAPPING b/hostsidetests/scopedstorage/TEST_MAPPING
new file mode 100644
index 0000000..8f4fbd1
--- /dev/null
+++ b/hostsidetests/scopedstorage/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsScopedStorageHostTest"
+    }
+  ]
+}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index 9fdb746..1e7a4fe 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -279,6 +279,21 @@
     }
 
     @Test
+    public void testOpenPendingAndTrashed() throws Exception {
+        runDeviceTest("testOpenPendingAndTrashed");
+    }
+
+    @Test
+    public void testDeletePendingAndTrashed() throws Exception {
+        runDeviceTest("testDeletePendingAndTrashed");
+    }
+
+    @Test
+    public void testListPendingAndTrashed() throws Exception {
+        runDeviceTest("testListPendingAndTrashed");
+    }
+
+    @Test
     public void testCanCreateDefaultDirectory() throws Exception {
         runDeviceTest("testCanCreateDefaultDirectory");
     }
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
index fedd206..3262766 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
@@ -18,9 +18,6 @@
 
 import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA1;
 import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2;
-import static android.scopedstorage.cts.lib.TestUtils.DCIM_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.EXTERNAL_STORAGE_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.MOVIES_DIR;
 import static android.scopedstorage.cts.lib.TestUtils.STR_DATA1;
 import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2;
 import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameDirectory;
@@ -92,9 +89,9 @@
     private static final String TAG = "LegacyFileAccessTest";
     static final String THIS_PACKAGE_NAME = InstrumentationRegistry.getContext().getPackageName();
 
-    static final String IMAGE_FILE_NAME = "FilePathAccessTest_file.jpg";
-    static final String VIDEO_FILE_NAME = "LegacyAccessTest_file.mp4";
-    static final String NONMEDIA_FILE_NAME = "LegacyAccessTest_file.pdf";
+    static final String IMAGE_FILE_NAME = "LegacyStorageTest_file.jpg";
+    static final String VIDEO_FILE_NAME = "LegacyStorageTest_file.mp4";
+    static final String NONMEDIA_FILE_NAME = "LegacyStorageTest_file.pdf";
 
     private static final TestApp TEST_APP_A = new TestApp("TestAppA",
             "android.scopedstorage.cts.testapp.A", 1, false, "CtsScopedStorageTestAppA.apk");
@@ -121,17 +118,17 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ false);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
         // Can create file under root dir
-        assertCanCreateFile(new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest.txt"));
+        assertCanCreateFile(new File(TestUtils.getExternalStorageDir(), "LegacyFileAccessTest.txt"));
 
         // Can create music file under DCIM
-        assertCanCreateFile(new File(TestUtils.DCIM_DIR, "LegacyFileAccessTest.mp3"));
+        assertCanCreateFile(new File(TestUtils.getDcimDir(), "LegacyFileAccessTest.mp3"));
 
         // Can create random file under external files dir
-        assertCanCreateFile(new File(InstrumentationRegistry.getContext().getExternalFilesDir(null),
+        assertCanCreateFile(new File(TestUtils.getExternalFilesDir(),
                 "LegacyFileAccessTest"));
 
         // However, even legacy apps can't create files under other app's directories
-        final File otherAppDir = new File(TestUtils.ANDROID_DATA_DIR, "com.android.shell");
+        final File otherAppDir = new File(TestUtils.getAndroidDataDir(), "com.android.shell");
         final File file = new File(otherAppDir, "LegacyFileAccessTest.txt");
 
         // otherAppDir was already created by the host test
@@ -151,10 +148,10 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ false);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
         // Can create a top-level direcotry
-        final File topLevelDir = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest");
+        final File topLevelDir = new File(TestUtils.getExternalStorageDir(), "LegacyFileAccessTest");
         assertCanCreateDir(topLevelDir);
 
-        final File otherAppDir = new File(TestUtils.ANDROID_DATA_DIR, "com.android.shell");
+        final File otherAppDir = new File(TestUtils.getAndroidDataDir(), "com.android.shell");
 
         // However, even legacy apps can't create dirs under other app's directories
         final File subDir = new File(otherAppDir, "LegacyFileAccessTest");
@@ -162,7 +159,7 @@
         assertThat(subDir.mkdir()).isFalse();
 
         // Try to list a directory and fail because it requires READ permission
-        assertThat(TestUtils.MUSIC_DIR.list()).isNull();
+        assertThat(TestUtils.getMusicDir().list()).isNull();
     }
 
     /**
@@ -173,7 +170,8 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ false);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ false);
         // Can't create file under root dir
-        final File newTxtFile = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest.txt");
+        final File newTxtFile = new File(TestUtils.getExternalStorageDir(),
+                "LegacyFileAccessTest.txt");
         try {
             newTxtFile.createNewFile();
             fail("File creation expected to fail: " + newTxtFile);
@@ -181,7 +179,7 @@
         }
 
         // Can't create music file under /MUSIC
-        final File newMusicFile = new File(TestUtils.MUSIC_DIR, "LegacyFileAccessTest.mp3");
+        final File newMusicFile = new File(TestUtils.getMusicDir(), "LegacyFileAccessTest.mp3");
         try {
             newMusicFile.createNewFile();
             fail("File creation expected to fail: " + newMusicFile);
@@ -189,11 +187,12 @@
         }
 
         // Can't create a top-level direcotry
-        final File topLevelDir = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest");
+        final File topLevelDir = new File(TestUtils.getExternalStorageDir(), "LegacyFileAccessTest");
         assertThat(topLevelDir.mkdir()).isFalse();
 
         // Can't read existing file
-        final File existingFile = new File(EXTERNAL_STORAGE_DIR, "LegacyAccessHostTest_shell");
+        final File existingFile = new File(TestUtils.getExternalStorageDir(),
+                "LegacyAccessHostTest_shell");
         try {
             Os.open(existingFile.getPath(), OsConstants.O_RDONLY, /*mode*/ 0);
             fail("Opening file for read expected to fail: " + existingFile);
@@ -204,29 +203,29 @@
         assertThat(existingFile.delete()).isFalse();
 
         // try to list a directory and fail
-        assertThat(TestUtils.MUSIC_DIR.list()).isNull();
-        assertThat(EXTERNAL_STORAGE_DIR.list()).isNull();
+        assertThat(TestUtils.getMusicDir().list()).isNull();
+        assertThat(TestUtils.getExternalStorageDir().list()).isNull();
 
         // However, even without permissions, we can access our own external dir
         final File fileInDataDir =
-                new File(InstrumentationRegistry.getContext().getExternalFilesDir(null),
+                new File(TestUtils.getExternalFilesDir(),
                         "LegacyFileAccessTest");
         try {
             assertThat(fileInDataDir.createNewFile()).isTrue();
             assertThat(Arrays.asList(fileInDataDir.getParentFile().list()))
-                    .containsExactly("LegacyFileAccessTest");
+                    .contains("LegacyFileAccessTest");
         } finally {
             fileInDataDir.delete();
         }
 
         // we can access our own external media directory without permissions.
         final File fileInMediaDir =
-                new File(InstrumentationRegistry.getContext().getExternalMediaDirs()[0],
+                new File(TestUtils.getExternalMediaDir(),
                         "LegacyFileAccessTest");
         try {
             assertThat(fileInMediaDir.createNewFile()).isTrue();
             assertThat(Arrays.asList(fileInMediaDir.getParentFile().list()))
-                    .containsExactly("LegacyFileAccessTest");
+                    .contains("LegacyFileAccessTest");
         } finally {
             fileInMediaDir.delete();
         }
@@ -238,10 +237,11 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ false);
         // can list directory content
-        assertThat(TestUtils.MUSIC_DIR.list()).isNotNull();
+        assertThat(TestUtils.getMusicDir().list()).isNotNull();
 
         // try to write a file and fail
-        final File existingFile = new File(EXTERNAL_STORAGE_DIR, "LegacyAccessHostTest_shell");
+        final File existingFile = new File(TestUtils.getExternalStorageDir(),
+                "LegacyAccessHostTest_shell");
 
         // can open file for read
         FileDescriptor fd = null;
@@ -261,7 +261,7 @@
         }
 
         // try to create file and fail, because it requires WRITE
-        final File newFile = new File(TestUtils.MUSIC_DIR, "LegacyFileAccessTest.mp3");
+        final File newFile = new File(TestUtils.getMusicDir(), "LegacyFileAccessTest.mp3");
         try {
             newFile.createNewFile();
             fail("Creating file expected to fail: " + newFile);
@@ -269,7 +269,7 @@
         }
 
         // try to mkdir and fail, because it requires WRITE
-        final File newDir = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest");
+        final File newDir = new File(TestUtils.getExternalStorageDir(), "LegacyFileAccessTest");
         try {
             assertThat(newDir.mkdir()).isFalse();
         } finally {
@@ -286,7 +286,7 @@
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ false);
 
         // can list a non-media file created by other package.
-        assertThat(Arrays.asList(EXTERNAL_STORAGE_DIR.list()))
+        assertThat(Arrays.asList(TestUtils.getExternalStorageDir().list()))
                 .contains("LegacyAccessHostTest_shell");
     }
 
@@ -299,11 +299,13 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File musicFile1 = new File(TestUtils.DCIM_DIR, "LegacyFileAccessTest.mp3");
-        final File musicFile2 = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest.mp3");
-        final File musicFile3 = new File(TestUtils.MOVIES_DIR, "LegacyFileAccessTest.mp3");
-        final File nonMediaDir1 = new File(TestUtils.DCIM_DIR, "LegacyFileAccessTest");
-        final File nonMediaDir2 = new File(EXTERNAL_STORAGE_DIR, "LegacyFileAccessTest");
+        final File musicFile1 = new File(TestUtils.getDcimDir(), "LegacyFileAccessTest.mp3");
+        final File musicFile2 = new File(TestUtils.getExternalStorageDir(),
+                "LegacyFileAccessTest.mp3");
+        final File musicFile3 = new File(TestUtils.getMoviesDir(), "LegacyFileAccessTest.mp3");
+        final File nonMediaDir1 = new File(TestUtils.getDcimDir(), "LegacyFileAccessTest");
+        final File nonMediaDir2 = new File(TestUtils.getExternalStorageDir(),
+                "LegacyFileAccessTest");
         final File pdfFile1 = new File(nonMediaDir1, "LegacyFileAccessTest.pdf");
         final File pdfFile2 = new File(nonMediaDir2, "LegacyFileAccessTest.pdf");
         try {
@@ -340,13 +342,14 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ false);
 
-        final File shellFile1 = new File(EXTERNAL_STORAGE_DIR, "LegacyAccessHostTest_shell");
-        final File shellFile2 = new File(TestUtils.DOWNLOAD_DIR, "LegacyFileAccessTest_shell");
+        final File shellFile1 = new File(TestUtils.getExternalStorageDir(),
+                "LegacyAccessHostTest_shell");
+        final File shellFile2 = new File(TestUtils.getDownloadDir(), "LegacyFileAccessTest_shell");
         final File mediaFile1 =
-                new File(InstrumentationRegistry.getContext().getExternalMediaDirs()[0],
+                new File(TestUtils.getExternalMediaDir(),
                         "LegacyFileAccessTest1");
         final File mediaFile2 =
-                new File(InstrumentationRegistry.getContext().getExternalMediaDirs()[0],
+                new File(TestUtils.getExternalMediaDir(),
                         "LegacyFileAccessTest2");
         try {
             // app can't rename shell file.
@@ -373,13 +376,14 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ false);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ false);
 
-        final File shellFile1 = new File(EXTERNAL_STORAGE_DIR, "LegacyAccessHostTest_shell");
-        final File shellFile2 = new File(TestUtils.DOWNLOAD_DIR, "LegacyFileAccessTest_shell");
+        final File shellFile1 = new File(TestUtils.getExternalStorageDir(),
+                "LegacyAccessHostTest_shell");
+        final File shellFile2 = new File(TestUtils.getDownloadDir(), "LegacyFileAccessTest_shell");
         final File mediaFile1 =
-                new File(InstrumentationRegistry.getContext().getExternalMediaDirs()[0],
+                new File(TestUtils.getExternalMediaDir(),
                         "LegacyFileAccessTest1");
         final File mediaFile2 =
-                new File(InstrumentationRegistry.getContext().getExternalMediaDirs()[0],
+                new File(TestUtils.getExternalMediaDir(),
                         "LegacyFileAccessTest2");
         try {
             // app can't rename shell file.
@@ -404,8 +408,8 @@
     @Test
     public void testRenameDirectoryAndUpdateDB_hasW() throws Exception {
         final String testDirectoryName = "LegacyFileAccessTestDirectory";
-        File directoryOldPath = new File(DCIM_DIR, testDirectoryName);
-        File directoryNewPath = new File(MOVIES_DIR, testDirectoryName);
+        File directoryOldPath = new File(TestUtils.getDcimDir(), testDirectoryName);
+        File directoryNewPath = new File(TestUtils.getMoviesDir(), testDirectoryName);
         try {
             if (directoryOldPath.exists()) {
                 executeShellCommand("rm -r " + directoryOldPath.getPath());
@@ -435,8 +439,8 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File videoFile = new File(EXTERNAL_STORAGE_DIR, VIDEO_FILE_NAME);
-        final File otherAppPdfFile = new File(TestUtils.DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
+        final File videoFile = new File(TestUtils.getExternalStorageDir(), VIDEO_FILE_NAME);
+        final File otherAppPdfFile = new File(TestUtils.getDownloadDir(), NONMEDIA_FILE_NAME);
 
         try {
             assertThat(videoFile.createNewFile()).isTrue();
@@ -471,13 +475,13 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File videoFile = new File(EXTERNAL_STORAGE_DIR, VIDEO_FILE_NAME);
+        final File videoFile = new File(TestUtils.getExternalStorageDir(), VIDEO_FILE_NAME);
         try {
             assertThat(videoFile.createNewFile()).isTrue();
 
             installApp(TEST_APP_A, true);
             // videoFile is inserted to database, non-legacy app can see this videoFile on 'ls'.
-            assertThat(listAs(TEST_APP_A, EXTERNAL_STORAGE_DIR.getAbsolutePath()))
+            assertThat(listAs(TEST_APP_A, TestUtils.getExternalStorageDir().getAbsolutePath()))
                     .contains(VIDEO_FILE_NAME);
 
             // videoFile is in database, row ID for videoFile can not be -1.
@@ -498,8 +502,8 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File videoFile = new File(TestUtils.DCIM_DIR, VIDEO_FILE_NAME);
-        final File renamedVideoFile = new File(TestUtils.DCIM_DIR, "Renamed_" + VIDEO_FILE_NAME);
+        final File videoFile = new File(TestUtils.getDcimDir(), VIDEO_FILE_NAME);
+        final File renamedVideoFile = new File(TestUtils.getDcimDir(), "Renamed_" + VIDEO_FILE_NAME);
         final ContentResolver cr = getContentResolver();
 
         try {
@@ -531,8 +535,8 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File imageFile = new File(TestUtils.DCIM_DIR, IMAGE_FILE_NAME);
-        final File temporaryImageFile = new File(TestUtils.DCIM_DIR, IMAGE_FILE_NAME + "_.tmp");
+        final File imageFile = new File(TestUtils.getDcimDir(), IMAGE_FILE_NAME);
+        final File temporaryImageFile = new File(TestUtils.getDcimDir(), IMAGE_FILE_NAME + "_.tmp");
         final ContentResolver cr = getContentResolver();
 
         try {
@@ -574,9 +578,9 @@
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
         pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File directoryNoMedia = new File(TestUtils.DCIM_DIR, ".directoryNoMedia");
+        final File directoryNoMedia = new File(TestUtils.getDcimDir(), ".directoryNoMedia");
         final File imageInNoMediaDir = new File(directoryNoMedia, IMAGE_FILE_NAME);
-        final File renamedImageInDCIM = new File(TestUtils.DCIM_DIR, IMAGE_FILE_NAME);
+        final File renamedImageInDCIM = new File(TestUtils.getDcimDir(), IMAGE_FILE_NAME);
         final File noMediaFile = new File(directoryNoMedia, ".nomedia");
         final ContentResolver cr = getContentResolver();
 
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index ce546e4..b23d52f 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -38,6 +38,7 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
@@ -67,9 +68,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
 
 /**
  * General helper functions for ScopedStorageTest tests.
@@ -96,37 +98,7 @@
     public static final byte[] BYTES_DATA2 = STR_DATA2.getBytes();
 
     // Root of external storage
-    public static final File EXTERNAL_STORAGE_DIR = Environment.getExternalStorageDirectory();
-    // Default top-level directories
-    public static final File ALARMS_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_ALARMS);
-    public static final File ANDROID_DIR = new File(EXTERNAL_STORAGE_DIR, "Android");
-    public static final File AUDIOBOOKS_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_AUDIOBOOKS);
-    public static final File DCIM_DIR = new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_DCIM);
-    public static final File DOCUMENTS_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_DOCUMENTS);
-    public static final File DOWNLOAD_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_DOWNLOADS);
-    public static final File MUSIC_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_MUSIC);
-    public static final File MOVIES_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_MOVIES);
-    public static final File NOTIFICATIONS_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_NOTIFICATIONS);
-    public static final File PICTURES_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_PICTURES);
-    public static final File PODCASTS_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_PODCASTS);
-    public static final File RINGTONES_DIR =
-            new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_RINGTONES);
-
-    public static final File[] DEFAULT_TOP_LEVEL_DIRS = new File[] {ALARMS_DIR, ANDROID_DIR,
-            AUDIOBOOKS_DIR, DCIM_DIR, DOCUMENTS_DIR, DOWNLOAD_DIR, MUSIC_DIR, MOVIES_DIR,
-            NOTIFICATIONS_DIR, PICTURES_DIR, PODCASTS_DIR, RINGTONES_DIR};
-
-    public static final File ANDROID_DATA_DIR = new File(ANDROID_DIR, "data");
-    public static final File ANDROID_MEDIA_DIR = new File(ANDROID_DIR, "media");
+    private static File sExternalStorageDirectory = Environment.getExternalStorageDirectory();
 
     private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
     private static final long POLLING_SLEEP_MILLIS = 100;
@@ -135,11 +107,11 @@
      * Creates the top level default directories.
      *
      * <p>Those are usually created by MediaProvider, but some naughty tests might delete them
-     * and not restore them afterwards. so we make sure we create them before we make any
+     * and not restore them afterwards, so we make sure we create them before we make any
      * assumptions about their existence.
      */
     public static void setupDefaultDirectories() {
-        for (File dir : DEFAULT_TOP_LEVEL_DIRS) {
+        for (File dir : getDefaultTopLevelDirs()) {
             dir.mkdir();
         }
     }
@@ -259,6 +231,16 @@
     }
 
     /**
+     * Makes the given {@code testApp} open {@code file} for read or write.
+     *
+     * <p>This method drops shell permission identity.
+     */
+    public static boolean openFileAs(TestApp testApp, File file, boolean forWrite)
+            throws Exception {
+        return openFileAs(testApp, file.getAbsolutePath(), forWrite);
+    }
+
+    /**
      * Makes the given {@code testApp} open a file for read or write.
      *
      * <p>This method drops shell permission identity.
@@ -378,7 +360,16 @@
     }
 
     /**
-     * Queries {@link ContentResolver} for a file and returns a {@link Cursor} with the given
+     * Queries {@link ContentResolver} for a video file and returns a {@link Cursor} with the given
+     * columns.
+     */
+    @NonNull
+    public static Cursor queryVideoFile(File file, String... projection) {
+        return queryFile(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, file, projection);
+    }
+
+    /**
+     * Queries {@link ContentResolver} for an image file and returns a {@link Cursor} with the given
      * columns.
      */
     @NonNull
@@ -432,6 +423,21 @@
     }
 
     /**
+     * Deletes db rows and files corresponding to uri through {@link ContentResolver} and
+     * {@link MediaStore} APIs.
+     */
+    public static void deleteWithMediaProviderNoThrow(Uri... uris) {
+        for (Uri uri : uris) {
+            if (uri == null) continue;
+
+            try {
+                getContentResolver().delete(uri, Bundle.EMPTY);
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    /**
      * Renames the given file through {@link ContentResolver} and {@link MediaStore} APIs,
      * and asserts that the file was updated in the database.
      */
@@ -601,28 +607,19 @@
      * Polls for external storage to be mounted.
      */
     public static void pollForExternalStorageState() throws Exception {
-        for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
-            if (Environment.getExternalStorageState(Environment.getExternalStorageDirectory())
-                            .equals(Environment.MEDIA_MOUNTED)) {
-                return;
-            }
-            Thread.sleep(POLLING_SLEEP_MILLIS);
-        }
-        fail("Timed out while waiting for ExternalStorageState to be MEDIA_MOUNTED");
+        pollForCondition(
+                () -> Environment.getExternalStorageState(getExternalStorageDir())
+                        .equals(Environment.MEDIA_MOUNTED),
+                "Timed out while waiting for ExternalStorageState to be MEDIA_MOUNTED");
     }
 
     /**
      * Polls until we're granted or denied a given permission.
      */
     public static void pollForPermission(String perm, boolean granted) throws Exception {
-        for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
-            if (granted == checkPermissionAndAppOp(perm)) {
-                return;
-            }
-            Thread.sleep(POLLING_SLEEP_MILLIS);
-        }
-        fail("Timed out while waiting for permission " + perm + " to be "
-                + (granted ? "granted" : "revoked"));
+        pollForCondition(() -> granted == checkPermissionAndAppOp(perm),
+                "Timed out while waiting for permission " + perm + " to be "
+                        + (granted ? "granted" : "revoked"));
     }
 
     /**
@@ -646,6 +643,137 @@
         }
     }
 
+    /**
+     * Asserts that {@code dir} is a directory and that it doesn't contain any of
+     * {@code unexpectedContent}
+     */
+    public static void assertDirectoryDoesNotContain(@NonNull File dir, File... unexpectedContent) {
+        assertThat(dir.isDirectory()).isTrue();
+        assertThat(Arrays.asList(dir.listFiles())).containsNoneIn(unexpectedContent);
+    }
+
+    /**
+     * Asserts that {@code dir} is a directory and that it contains all of {@code expectedContent}
+     */
+    public static void assertDirectoryContains(@NonNull File dir, File... expectedContent) {
+        assertThat(dir.isDirectory()).isTrue();
+        assertThat(Arrays.asList(dir.listFiles())).containsAllIn(expectedContent);
+    }
+
+    public static File getExternalStorageDir() {
+        return sExternalStorageDirectory;
+    }
+
+    public static void setExternalStorageVolume(@NonNull String volName) {
+        sExternalStorageDirectory = new File("/storage/" + volName);
+    }
+
+    /**
+     * Resets the root directory of external storage to the default.
+     *
+     * @see Environment#getExternalStorageDirectory()
+     */
+    public static void resetDefaultExternalStorageVolume() {
+        sExternalStorageDirectory = Environment.getExternalStorageDirectory();
+    }
+
+    /**
+     * Creates and returns the Android data sub-directory belonging to the calling package.
+     */
+    public static File getExternalFilesDir() {
+        final String packageName = getContext().getPackageName();
+        final File res = new File(getAndroidDataDir(), packageName + "/files");
+        if (!res.equals(getContext().getExternalFilesDir(null))) {
+            res.mkdirs();
+        }
+        return res;
+    }
+
+    /**
+     * Creates and returns the Android media sub-directory belonging to the calling package.
+     */
+    public static File getExternalMediaDir() {
+        final String packageName = getContext().getPackageName();
+        final File res = new File(getAndroidMediaDir(), packageName);
+        if (!res.equals(getContext().getExternalMediaDirs()[0])) {
+            res.mkdirs();
+        }
+        return res;
+    }
+
+    public static File getAlarmsDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_ALARMS);
+    }
+
+    public static File getAndroidDir() {
+        return new File(getExternalStorageDir(),
+                "Android");
+    }
+
+    public static File getAudiobooksDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_AUDIOBOOKS);
+    }
+
+    public static File getDcimDir() {
+        return new File(getExternalStorageDir(), Environment.DIRECTORY_DCIM);
+    }
+
+    public static File getDocumentsDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_DOCUMENTS);
+    }
+
+    public static File getDownloadDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_DOWNLOADS);
+    }
+
+    public static File getMusicDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_MUSIC);
+    }
+
+    public static File getMoviesDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_MOVIES);
+    }
+
+    public static File getNotificationsDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_NOTIFICATIONS);
+    }
+
+    public static File getPicturesDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_PICTURES);
+    }
+
+    public static File getPodcastsDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_PODCASTS);
+    }
+
+    public static File getRingtonesDir() {
+        return new File(getExternalStorageDir(),
+                Environment.DIRECTORY_RINGTONES);
+    }
+
+    public static File getAndroidDataDir() {
+        return new File(getAndroidDir(), "data");
+    }
+
+    public static File getAndroidMediaDir() {
+        return new File(getAndroidDir(), "media");
+    }
+
+    public static File[] getDefaultTopLevelDirs() {
+        return new File [] { getAlarmsDir(), getAndroidDir(), getAudiobooksDir(), getDcimDir(),
+                getDocumentsDir(), getDownloadDir(), getMusicDir(), getMoviesDir(),
+                getNotificationsDir(), getPicturesDir(), getPodcastsDir(), getRingtonesDir() };
+    }
+
     private static void assertInputStreamContent(InputStream in, byte[] expectedContent)
             throws IOException {
         assertThat(ByteStreams.toByteArray(in)).isEqualTo(expectedContent);
@@ -830,22 +958,37 @@
 
     @NonNull
     private static Cursor queryFile(@NonNull Uri uri, @NonNull File file, String... projection) {
-        final Cursor c = getContentResolver().query(uri, projection,
-                /*selection*/ MediaStore.MediaColumns.DATA + " = ?",
-                /*selectionArgs*/ new String[] {file.getAbsolutePath()},
-                /*sortOrder*/ null);
+        Bundle queryArgs = new Bundle();
+        queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+                MediaStore.MediaColumns.DATA + " = ?");
+        queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
+                new String[] { file.getAbsolutePath() });
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
+        queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+
+        final Cursor c = getContentResolver().query(uri, projection, queryArgs, null);
         assertThat(c).isNotNull();
         return c;
     }
 
-    /**
-     * Asserts that {@code dir} is a directory and that it contains all of {@code expectedContent}
-     */
-    public static void assertDirectoryContains(@NonNull File dir, File... expectedContent) {
-        assertThat(dir.isDirectory()).isTrue();
-        final List<File> actualContent = Arrays.asList(dir.listFiles());
-        for (File f : expectedContent) {
-            assertThat(actualContent).contains(f);
+    private static boolean partitionDisk() {
+        try {
+            final String listDisks = executeShellCommand("sm list-disks").trim();
+            executeShellCommand("sm partition " + listDisks + " public");
+            return true;
+        } catch (Exception e) {
+            return false;
         }
     }
+
+    private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)
+            throws Exception {
+        for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
+            if (condition.get()) {
+                return;
+            }
+            Thread.sleep(POLLING_SLEEP_MILLIS);
+        }
+        throw new TimeoutException(errorMessage);
+    }
 }
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 2867aec..606e15a 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -23,22 +23,8 @@
 import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMismatch;
 import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata;
 import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromRawResource;
-import static android.scopedstorage.cts.lib.TestUtils.ALARMS_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.ANDROID_DATA_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.ANDROID_MEDIA_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.AUDIOBOOKS_DIR;
 import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA1;
 import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2;
-import static android.scopedstorage.cts.lib.TestUtils.DCIM_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.DEFAULT_TOP_LEVEL_DIRS;
-import static android.scopedstorage.cts.lib.TestUtils.DOCUMENTS_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.DOWNLOAD_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.MOVIES_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.MUSIC_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.NOTIFICATIONS_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.PICTURES_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.PODCASTS_DIR;
-import static android.scopedstorage.cts.lib.TestUtils.RINGTONES_DIR;
 import static android.scopedstorage.cts.lib.TestUtils.STR_DATA1;
 import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2;
 import static android.scopedstorage.cts.lib.TestUtils.allowAppOpsToUid;
@@ -55,12 +41,31 @@
 import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow;
 import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively;
 import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProvider;
+import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
 import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid;
 import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
+import static android.scopedstorage.cts.lib.TestUtils.getAlarmsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidDataDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidMediaDir;
+import static android.scopedstorage.cts.lib.TestUtils.getAudiobooksDir;
 import static android.scopedstorage.cts.lib.TestUtils.getContentResolver;
+import static android.scopedstorage.cts.lib.TestUtils.getDcimDir;
+import static android.scopedstorage.cts.lib.TestUtils.getDefaultTopLevelDirs;
+import static android.scopedstorage.cts.lib.TestUtils.getDocumentsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getDownloadDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalFilesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalMediaDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalStorageDir;
 import static android.scopedstorage.cts.lib.TestUtils.getFileMimeTypeFromDatabase;
 import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase;
 import static android.scopedstorage.cts.lib.TestUtils.getFileUri;
+import static android.scopedstorage.cts.lib.TestUtils.getMoviesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getMusicDir;
+import static android.scopedstorage.cts.lib.TestUtils.getNotificationsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getPicturesDir;
+import static android.scopedstorage.cts.lib.TestUtils.getPodcastsDir;
+import static android.scopedstorage.cts.lib.TestUtils.getRingtonesDir;
 import static android.scopedstorage.cts.lib.TestUtils.grantPermission;
 import static android.scopedstorage.cts.lib.TestUtils.installApp;
 import static android.scopedstorage.cts.lib.TestUtils.installAppWithStoragePermissions;
@@ -70,6 +75,7 @@
 import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
 import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
 import static android.scopedstorage.cts.lib.TestUtils.queryImageFile;
+import static android.scopedstorage.cts.lib.TestUtils.queryVideoFile;
 import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestApp;
 import static android.scopedstorage.cts.lib.TestUtils.revokePermission;
 import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
@@ -90,15 +96,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.Manifest;
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
@@ -129,19 +141,15 @@
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 public class ScopedStorageTest {
     static final String TAG = "ScopedStorageTest";
     static final String THIS_PACKAGE_NAME = getContext().getPackageName();
 
-    static final File EXTERNAL_STORAGE_DIR = Environment.getExternalStorageDirectory();
-
     static final String TEST_DIRECTORY_NAME = "ScopedStorageTestDirectory";
 
-    static final File EXTERNAL_FILES_DIR = getContext().getExternalFilesDir(null);
-    static final File EXTERNAL_MEDIA_DIR = getContext().getExternalMediaDirs()[0];
-
     static final String AUDIO_FILE_NAME = "ScopedStorageTest_file.mp3";
     static final String PLAYLIST_FILE_NAME = "ScopedStorageTest_file.m3u";
     static final String SUBTITLE_FILE_NAME = "ScopedStorageTest_file.srt";
@@ -150,8 +158,6 @@
     static final String NONMEDIA_FILE_NAME = "ScopedStorageTest_file.pdf";
 
     static final String FILE_CREATION_ERROR_MESSAGE = "No such file or directory";
-    private static final File ANDROID_DIR =
-            new File(Environment.getExternalStorageDirectory(), "Android");
 
     private static final TestApp TEST_APP_A = new TestApp("TestAppA",
             "android.scopedstorage.cts.testapp.A", 1, false, "CtsScopedStorageTestAppA.apk");
@@ -172,7 +178,7 @@
         assumeTrue(getBoolean("persist.sys.fuse", false));
 
         pollForExternalStorageState();
-        EXTERNAL_FILES_DIR.mkdirs();
+        getExternalFilesDir().mkdirs();
     }
 
     /**
@@ -188,66 +194,72 @@
      */
     @Test
     public void testTypePathConformity() throws Exception {
+        final File dcimDir = getDcimDir();
+        final File documentsDir = getDocumentsDir();
+        final File downloadDir = getDownloadDir();
+        final File moviesDir = getMoviesDir();
+        final File musicDir = getMusicDir();
+        final File picturesDir = getPicturesDir();
         // Only audio files can be created in Music
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MUSIC_DIR, NONMEDIA_FILE_NAME).createNewFile(); });
+                () -> { new File(musicDir, NONMEDIA_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MUSIC_DIR, VIDEO_FILE_NAME).createNewFile(); });
+                () -> { new File(musicDir, VIDEO_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MUSIC_DIR, IMAGE_FILE_NAME).createNewFile(); });
+                () -> { new File(musicDir, IMAGE_FILE_NAME).createNewFile(); });
         // Only video files can be created in Movies
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MOVIES_DIR, NONMEDIA_FILE_NAME).createNewFile(); });
+                () -> { new File(moviesDir, NONMEDIA_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MOVIES_DIR, AUDIO_FILE_NAME).createNewFile(); });
+                () -> { new File(moviesDir, AUDIO_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(MOVIES_DIR, IMAGE_FILE_NAME).createNewFile(); });
+                () -> { new File(moviesDir, IMAGE_FILE_NAME).createNewFile(); });
         // Only image and video files can be created in DCIM
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(DCIM_DIR, NONMEDIA_FILE_NAME).createNewFile(); });
+                () -> { new File(dcimDir, NONMEDIA_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(DCIM_DIR, AUDIO_FILE_NAME).createNewFile(); });
+                () -> { new File(dcimDir, AUDIO_FILE_NAME).createNewFile(); });
         // Only image and video files can be created in Pictures
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(PICTURES_DIR, NONMEDIA_FILE_NAME).createNewFile(); });
+                () -> { new File(picturesDir, NONMEDIA_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(PICTURES_DIR, AUDIO_FILE_NAME).createNewFile(); });
+                () -> { new File(picturesDir, AUDIO_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(PICTURES_DIR, PLAYLIST_FILE_NAME).createNewFile(); });
+                () -> { new File(picturesDir, PLAYLIST_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(DCIM_DIR, SUBTITLE_FILE_NAME).createNewFile(); });
+                () -> { new File(dcimDir, SUBTITLE_FILE_NAME).createNewFile(); });
 
-        assertCanCreateFile(new File(ALARMS_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(AUDIOBOOKS_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(DCIM_DIR, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(DCIM_DIR, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(DOCUMENTS_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(DOCUMENTS_DIR, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(DOCUMENTS_DIR, NONMEDIA_FILE_NAME));
-        assertCanCreateFile(new File(DOCUMENTS_DIR, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(DOWNLOAD_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(DOWNLOAD_DIR, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME));
-        assertCanCreateFile(new File(DOWNLOAD_DIR, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(MOVIES_DIR, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(MOVIES_DIR, SUBTITLE_FILE_NAME));
-        assertCanCreateFile(new File(MUSIC_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(MUSIC_DIR, PLAYLIST_FILE_NAME));
-        assertCanCreateFile(new File(NOTIFICATIONS_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(PICTURES_DIR, IMAGE_FILE_NAME));
-        assertCanCreateFile(new File(PICTURES_DIR, VIDEO_FILE_NAME));
-        assertCanCreateFile(new File(PODCASTS_DIR, AUDIO_FILE_NAME));
-        assertCanCreateFile(new File(RINGTONES_DIR, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(getAlarmsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(getAudiobooksDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(dcimDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(dcimDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, NONMEDIA_FILE_NAME));
+        assertCanCreateFile(new File(documentsDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, NONMEDIA_FILE_NAME));
+        assertCanCreateFile(new File(downloadDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(moviesDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(moviesDir, SUBTITLE_FILE_NAME));
+        assertCanCreateFile(new File(musicDir, AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(musicDir, PLAYLIST_FILE_NAME));
+        assertCanCreateFile(new File(getNotificationsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(picturesDir, IMAGE_FILE_NAME));
+        assertCanCreateFile(new File(picturesDir, VIDEO_FILE_NAME));
+        assertCanCreateFile(new File(getPodcastsDir(), AUDIO_FILE_NAME));
+        assertCanCreateFile(new File(getRingtonesDir(), AUDIO_FILE_NAME));
 
         // No file whatsoever can be created in the top level directory
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(EXTERNAL_STORAGE_DIR, NONMEDIA_FILE_NAME).createNewFile(); });
+                () -> { new File(getExternalStorageDir(), NONMEDIA_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(EXTERNAL_STORAGE_DIR, AUDIO_FILE_NAME).createNewFile(); });
+                () -> { new File(getExternalStorageDir(), AUDIO_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(EXTERNAL_STORAGE_DIR, IMAGE_FILE_NAME).createNewFile(); });
+                () -> { new File(getExternalStorageDir(), IMAGE_FILE_NAME).createNewFile(); });
         assertThrows(IOException.class, "Operation not permitted",
-                () -> { new File(EXTERNAL_STORAGE_DIR, VIDEO_FILE_NAME).createNewFile(); });
+                () -> { new File(getExternalStorageDir(), VIDEO_FILE_NAME).createNewFile(); });
     }
 
     /**
@@ -256,7 +268,7 @@
      */
     @Test
     public void testCreateFileInAppExternalDir() throws Exception {
-        final File file = new File(EXTERNAL_FILES_DIR, "text.txt");
+        final File file = new File(getExternalFilesDir(), "text.txt");
         try {
             assertThat(file.createNewFile()).isTrue();
             assertThat(file.delete()).isTrue();
@@ -283,7 +295,7 @@
     public void testCreateFileInOtherAppExternalDir() throws Exception {
         // Creating a file in a non existent package dir should return ENOENT, as expected
         final File nonexistentPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
         final File file1 = new File(nonexistentPackageFileDir, NONMEDIA_FILE_NAME);
         assertThrows(
                 IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> { file1.createNewFile(); });
@@ -292,7 +304,7 @@
         // leaking installed app names, and we know the following directory exists because shell
         // mkdirs it in test setup
         final File shellPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
         final File file2 = new File(shellPackageFileDir, NONMEDIA_FILE_NAME);
         assertThrows(
                 IOException.class, FILE_CREATION_ERROR_MESSAGE, () -> { file1.createNewFile(); });
@@ -303,7 +315,7 @@
      */
     @Test
     public void testContributeMediaFile() throws Exception {
-        final File imageFile = new File(DCIM_DIR, IMAGE_FILE_NAME);
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
 
         ContentResolver cr = getContentResolver();
         final String selection =
@@ -358,13 +370,14 @@
 
     @Test
     public void testCreateAndDeleteEmptyDir() throws Exception {
+        final File externalFilesDir = getExternalFilesDir();
         // Remove directory in order to create it again
-        EXTERNAL_FILES_DIR.delete();
+        externalFilesDir.delete();
 
         // Can create own external files dir
-        assertThat(EXTERNAL_FILES_DIR.mkdir()).isTrue();
+        assertThat(externalFilesDir.mkdir()).isTrue();
 
-        final File dir1 = new File(EXTERNAL_FILES_DIR, "random_dir");
+        final File dir1 = new File(externalFilesDir, "random_dir");
         // Can create dirs inside it
         assertThat(dir1.mkdir()).isTrue();
 
@@ -375,13 +388,13 @@
         // And can delete them all
         assertThat(dir2.delete()).isTrue();
         assertThat(dir1.delete()).isTrue();
-        assertThat(EXTERNAL_FILES_DIR.delete()).isTrue();
+        assertThat(externalFilesDir.delete()).isTrue();
 
         // Can't create external dir for other apps
         final File nonexistentPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
         final File shellPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+                externalFilesDir.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
 
         assertThat(nonexistentPackageFileDir.mkdir()).isFalse();
         assertThat(shellPackageFileDir.mkdir()).isFalse();
@@ -389,8 +402,8 @@
 
     @Test
     public void testCantAccessOtherAppsContents() throws Exception {
-        final File mediaFile = new File(PICTURES_DIR, IMAGE_FILE_NAME);
-        final File nonMediaFile = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
+        final File mediaFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
         try {
             installApp(TEST_APP_A);
 
@@ -415,7 +428,7 @@
 
     @Test
     public void testCantDeleteOtherAppsContents() throws Exception {
-        final File dirInDownload = new File(DOWNLOAD_DIR, TEST_DIRECTORY_NAME);
+        final File dirInDownload = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
         final File mediaFile = new File(dirInDownload, IMAGE_FILE_NAME);
         final File nonMediaFile = new File(dirInDownload, NONMEDIA_FILE_NAME);
         try {
@@ -470,27 +483,26 @@
     public void testOpendirRestrictions() throws Exception {
         // Opening a non existent package directory should fail, as expected
         final File nonexistentPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "no.such.package"));
         assertThat(nonexistentPackageFileDir.list()).isNull();
 
         // Opening another package's external directory should fail as well, even if it exists
         final File shellPackageFileDir = new File(
-                EXTERNAL_FILES_DIR.getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
+                getExternalFilesDir().getPath().replace(THIS_PACKAGE_NAME, "com.android.shell"));
         assertThat(shellPackageFileDir.list()).isNull();
 
         // We can open our own external files directory
-        final String[] filesList = EXTERNAL_FILES_DIR.list();
+        final String[] filesList = getExternalFilesDir().list();
         assertThat(filesList).isNotNull();
-        assertThat(filesList).isEmpty();
 
         // We can open any public directory in external storage
-        assertThat(DCIM_DIR.list()).isNotNull();
-        assertThat(DOWNLOAD_DIR.list()).isNotNull();
-        assertThat(MOVIES_DIR.list()).isNotNull();
-        assertThat(MUSIC_DIR.list()).isNotNull();
+        assertThat(getDcimDir().list()).isNotNull();
+        assertThat(getDownloadDir().list()).isNotNull();
+        assertThat(getMoviesDir().list()).isNotNull();
+        assertThat(getMusicDir().list()).isNotNull();
 
         // We can open the root directory of external storage
-        final String[] topLevelDirs = EXTERNAL_STORAGE_DIR.list();
+        final String[] topLevelDirs = getExternalStorageDir().list();
         assertThat(topLevelDirs).isNotNull();
         // TODO(b/145287327): This check fails on a device with no visible files.
         // This can be fixed if we display default directories.
@@ -499,7 +511,7 @@
 
     @Test
     public void testLowLevelFileIO() throws Exception {
-        String filePath = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME).toString();
+        String filePath = new File(getDownloadDir(), NONMEDIA_FILE_NAME).toString();
         try {
             int createFlags = O_CREAT | O_RDWR;
             int createExclFlags = createFlags | O_EXCL;
@@ -544,7 +556,8 @@
      */
     @Test
     public void testListDirectoriesWithMediaFiles() throws Exception {
-        final File dir = new File(DCIM_DIR, TEST_DIRECTORY_NAME);
+        final File dcimDir = getDcimDir();
+        final File dir = new File(dcimDir, TEST_DIRECTORY_NAME);
         final File videoFile = new File(dir, VIDEO_FILE_NAME);
         final String videoFileName = videoFile.getName();
         try {
@@ -556,14 +569,14 @@
             installApp(TEST_APP_A);
             assertThat(createFileAs(TEST_APP_A, videoFile.getPath())).isTrue();
             // TEST_APP_A should see TEST_DIRECTORY in DCIM and new file in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_A, DCIM_DIR.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_A, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
             assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(videoFileName);
 
             // Install TEST_APP_B with storage permission.
             installAppWithStoragePermissions(TEST_APP_B);
             // TEST_APP_B with storage permission should see TEST_DIRECTORY in DCIM and new file
             // in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, DCIM_DIR.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
             assertThat(listAs(TEST_APP_B, dir.getPath())).containsExactly(videoFileName);
 
             // Revoke storage permission for TEST_APP_B
@@ -571,7 +584,7 @@
                     TEST_APP_B.getPackageName(), Manifest.permission.READ_EXTERNAL_STORAGE);
             // TEST_APP_B without storage permission should see TEST_DIRECTORY in DCIM and should
             // not see new file in new TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, DCIM_DIR.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, dcimDir.getPath())).contains(TEST_DIRECTORY_NAME);
             assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(videoFileName);
         } finally {
             uninstallAppNoThrow(TEST_APP_B);
@@ -586,7 +599,8 @@
      */
     @Test
     public void testListDirectoriesWithNonMediaFiles() throws Exception {
-        final File dir = new File(DOWNLOAD_DIR, TEST_DIRECTORY_NAME);
+        final File downloadDir = getDownloadDir();
+        final File dir = new File(downloadDir, TEST_DIRECTORY_NAME);
         final File pdfFile = new File(dir, NONMEDIA_FILE_NAME);
         final String pdfFileName = pdfFile.getName();
         try {
@@ -598,16 +612,16 @@
             installApp(TEST_APP_A);
             assertThat(createFileAs(TEST_APP_A, pdfFile.getPath())).isTrue();
 
-            // TEST_APP_A should see TEST_DIRECTORY in DOWNLOAD_DIR and new non media file in
+            // TEST_APP_A should see TEST_DIRECTORY in downloadDir and new non media file in
             // TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_A, DOWNLOAD_DIR.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_A, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
             assertThat(listAs(TEST_APP_A, dir.getPath())).containsExactly(pdfFileName);
 
             // Install TEST_APP_B with storage permission.
             installAppWithStoragePermissions(TEST_APP_B);
-            // TEST_APP_B with storage permission should see TEST_DIRECTORY in DOWNLOAD_DIR
+            // TEST_APP_B with storage permission should see TEST_DIRECTORY in downloadDir
             // and should not see new non media file in TEST_DIRECTORY.
-            assertThat(listAs(TEST_APP_B, DOWNLOAD_DIR.getPath())).contains(TEST_DIRECTORY_NAME);
+            assertThat(listAs(TEST_APP_B, downloadDir.getPath())).contains(TEST_DIRECTORY_NAME);
             assertThat(listAs(TEST_APP_B, dir.getPath())).doesNotContain(pdfFileName);
         } finally {
             uninstallAppNoThrow(TEST_APP_B);
@@ -623,7 +637,7 @@
     @Test
     public void testListFilesFromExternalFilesDirectory() throws Exception {
         final String packageName = THIS_PACKAGE_NAME;
-        final File videoFile = new File(EXTERNAL_FILES_DIR, NONMEDIA_FILE_NAME);
+        final File videoFile = new File(getExternalFilesDir(), NONMEDIA_FILE_NAME);
 
         try {
             // Create a file in app's external files directory
@@ -638,8 +652,9 @@
             // TEST_APP_A should not see other app's external files directory.
             installAppWithStoragePermissions(TEST_APP_A);
 
-            assertThrows(IOException.class, () -> listAs(TEST_APP_A, ANDROID_DATA_DIR.getPath()));
-            assertThrows(IOException.class, () -> listAs(TEST_APP_A, EXTERNAL_FILES_DIR.getPath()));
+            assertThrows(IOException.class, () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
+            assertThrows(IOException.class,
+                    () -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
         } finally {
             videoFile.delete();
             uninstallAppNoThrow(TEST_APP_A);
@@ -651,7 +666,7 @@
      */
     @Test
     public void testListFilesFromExternalMediaDirectory() throws Exception {
-        final File videoFile = new File(EXTERNAL_MEDIA_DIR, VIDEO_FILE_NAME);
+        final File videoFile = new File(getExternalMediaDir(), VIDEO_FILE_NAME);
 
         try {
             // Create a file in app's external media directory
@@ -666,9 +681,11 @@
             // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
             // TEST_APP_A with storage permission should see other app's external media directory.
             installAppWithStoragePermissions(TEST_APP_A);
-            // Apps with READ_EXTERNAL_STORAGE can list files in other app's external media directory.
-            assertThat(listAs(TEST_APP_A, ANDROID_MEDIA_DIR.getPath())).contains(THIS_PACKAGE_NAME);
-            assertThat(listAs(TEST_APP_A, EXTERNAL_MEDIA_DIR.getPath()))
+            // Apps with READ_EXTERNAL_STORAGE can list files in other app's external media
+            // directory.
+            assertThat(listAs(TEST_APP_A, getAndroidMediaDir().getPath()))
+                    .contains(THIS_PACKAGE_NAME);
+            assertThat(listAs(TEST_APP_A, getExternalMediaDir().getPath()))
                     .containsExactly(videoFile.getName());
         } finally {
             videoFile.delete();
@@ -681,8 +698,8 @@
      */
     @Test
     public void testListUnsupportedFileType() throws Exception {
-        final File pdfFile = new File(DCIM_DIR, NONMEDIA_FILE_NAME);
-        final File videoFile = new File(MUSIC_DIR, VIDEO_FILE_NAME);
+        final File pdfFile = new File(getDcimDir(), NONMEDIA_FILE_NAME);
+        final File videoFile = new File(getMusicDir(), VIDEO_FILE_NAME);
         try {
             // TEST_APP_A with storage permission should not see pdf file in DCIM
             executeShellCommand("touch " + pdfFile.getAbsolutePath());
@@ -690,13 +707,14 @@
             assertThat(MediaStore.scanFile(getContentResolver(), pdfFile)).isNotNull();
 
             installAppWithStoragePermissions(TEST_APP_A);
-            assertThat(listAs(TEST_APP_A, DCIM_DIR.getPath())).doesNotContain(NONMEDIA_FILE_NAME);
+            assertThat(listAs(TEST_APP_A, getDcimDir().getPath()))
+                    .doesNotContain(NONMEDIA_FILE_NAME);
 
             executeShellCommand("touch " + videoFile.getAbsolutePath());
             // We don't insert files to db for files created by shell.
             assertThat(MediaStore.scanFile(getContentResolver(), videoFile)).isNotNull();
             // TEST_APP_A with storage permission should see video file in Music directory.
-            assertThat(listAs(TEST_APP_A, MUSIC_DIR.getPath())).contains(VIDEO_FILE_NAME);
+            assertThat(listAs(TEST_APP_A, getMusicDir().getPath())).contains(VIDEO_FILE_NAME);
         } finally {
             executeShellCommand("rm " + pdfFile.getAbsolutePath());
             executeShellCommand("rm " + videoFile.getAbsolutePath());
@@ -706,7 +724,7 @@
 
     @Test
     public void testMetaDataRedaction() throws Exception {
-        File jpgFile = new File(PICTURES_DIR, "img_metadata.jpg");
+        File jpgFile = new File(getPicturesDir(), "img_metadata.jpg");
         try {
             if (jpgFile.exists()) {
                 assertThat(jpgFile.delete()).isTrue();
@@ -742,7 +760,7 @@
     @Test
     public void testOpenFilePathFirstWriteContentResolver() throws Exception {
         String displayName = "open_file_path_write_content_resolver.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -761,7 +779,7 @@
     @Test
     public void testOpenContentResolverFirstWriteContentResolver() throws Exception {
         String displayName = "open_content_resolver_write_content_resolver.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -780,7 +798,7 @@
     @Test
     public void testOpenFilePathFirstWriteFilePath() throws Exception {
         String displayName = "open_file_path_write_file_path.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -799,7 +817,7 @@
     @Test
     public void testOpenContentResolverFirstWriteFilePath() throws Exception {
         String displayName = "open_content_resolver_write_file_path.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -818,7 +836,7 @@
     @Test
     public void testOpenContentResolverWriteOnly() throws Exception {
         String displayName = "open_content_resolver_write_only.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -839,7 +857,7 @@
     @Test
     public void testOpenContentResolverDup() throws Exception {
         String displayName = "open_content_resolver_dup.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             file.delete();
@@ -865,7 +883,7 @@
     @Test
     public void testOpenContentResolverClose() throws Exception {
         String displayName = "open_content_resolver_close.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             byte[] readBuffer = new byte[10];
@@ -897,7 +915,7 @@
     @Test
     public void testContentResolverDelete() throws Exception {
         String displayName = "content_resolver_delete.jpg";
-        File file = new File(DCIM_DIR, displayName);
+        File file = new File(getDcimDir(), displayName);
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -915,8 +933,8 @@
     public void testContentResolverUpdate() throws Exception {
         String oldDisplayName = "content_resolver_update_old.jpg";
         String newDisplayName = "content_resolver_update_new.jpg";
-        File oldFile = new File(DCIM_DIR, oldDisplayName);
-        File newFile = new File(DCIM_DIR, newDisplayName);
+        File oldFile = new File(getDcimDir(), oldDisplayName);
+        File newFile = new File(getDcimDir(), newDisplayName);
 
         try {
             assertThat(oldFile.createNewFile()).isTrue();
@@ -936,24 +954,24 @@
 
     @Test
     public void testCreateLowerCaseDeleteUpperCase() throws Exception {
-        File upperCase = new File(DOWNLOAD_DIR, "CREATE_LOWER_DELETE_UPPER");
-        File lowerCase = new File(DOWNLOAD_DIR, "create_lower_delete_upper");
+        File upperCase = new File(getDownloadDir(), "CREATE_LOWER_DELETE_UPPER");
+        File lowerCase = new File(getDownloadDir(), "create_lower_delete_upper");
 
         createDeleteCreate(lowerCase, upperCase);
     }
 
     @Test
     public void testCreateUpperCaseDeleteLowerCase() throws Exception {
-        File upperCase = new File(DOWNLOAD_DIR, "CREATE_UPPER_DELETE_LOWER");
-        File lowerCase = new File(DOWNLOAD_DIR, "create_upper_delete_lower");
+        File upperCase = new File(getDownloadDir(), "CREATE_UPPER_DELETE_LOWER");
+        File lowerCase = new File(getDownloadDir(), "create_upper_delete_lower");
 
         createDeleteCreate(upperCase, lowerCase);
     }
 
     @Test
     public void testCreateMixedCaseDeleteDifferentMixedCase() throws Exception {
-        File mixedCase1 = new File(DOWNLOAD_DIR, "CrEaTe_MiXeD_dElEtE_mIxEd");
-        File mixedCase2 = new File(DOWNLOAD_DIR, "cReAtE_mIxEd_DeLeTe_MiXeD");
+        File mixedCase1 = new File(getDownloadDir(), "CrEaTe_MiXeD_dElEtE_mIxEd");
+        File mixedCase2 = new File(getDownloadDir(), "cReAtE_mIxEd_DeLeTe_MiXeD");
 
         createDeleteCreate(mixedCase1, mixedCase2);
     }
@@ -976,39 +994,39 @@
 
     @Test
     public void testReadStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(DCIM_DIR, "read_storage.jpg"),
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "read_storage.jpg"),
                 Manifest.permission.READ_EXTERNAL_STORAGE,
                 AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, /* forWrite */ false);
     }
 
     @Test
     public void testWriteStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C_LEGACY, new File(DCIM_DIR, "write_storage.jpg"),
+        testAppOpInvalidation(TEST_APP_C_LEGACY, new File(getDcimDir(), "write_storage.jpg"),
                 Manifest.permission.WRITE_EXTERNAL_STORAGE,
                 AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE, /* forWrite */ true);
     }
 
     @Test
     public void testManageStorageInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(DOWNLOAD_DIR, "manage_storage.pdf"),
+        testAppOpInvalidation(TEST_APP_C, new File(getDownloadDir(), "manage_storage.pdf"),
                 /* permission */ null, OPSTR_MANAGE_EXTERNAL_STORAGE, /* forWrite */ true);
     }
 
     @Test
     public void testWriteImagesInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(DCIM_DIR, "write_images.jpg"),
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_images.jpg"),
                 /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_IMAGES, /* forWrite */ true);
     }
 
     @Test
     public void testWriteVideoInvalidation() throws Exception {
-        testAppOpInvalidation(TEST_APP_C, new File(DCIM_DIR, "write_video.mp4"),
+        testAppOpInvalidation(TEST_APP_C, new File(getDcimDir(), "write_video.mp4"),
                 /* permission */ null, AppOpsManager.OPSTR_WRITE_MEDIA_VIDEO, /* forWrite */ true);
     }
 
     @Test
     public void testAccessMediaLocationInvalidation() throws Exception {
-        File imgFile = new File(DCIM_DIR, "access_media_location.jpg");
+        File imgFile = new File(getDcimDir(), "access_media_location.jpg");
 
         try {
             // Setup image with sensitive data on external storage
@@ -1050,7 +1068,7 @@
 
     @Test
     public void testAppUpdateInvalidation() throws Exception {
-        File file = new File(DCIM_DIR, "app_update.jpg");
+        File file = new File(getDcimDir(), "app_update.jpg");
         try {
             assertThat(file.createNewFile()).isTrue();
 
@@ -1078,7 +1096,7 @@
 
     @Test
     public void testAppReinstallInvalidation() throws Exception {
-        File file = new File(DCIM_DIR, "app_reinstall.jpg");
+        File file = new File(getDcimDir(), "app_reinstall.jpg");
 
         try {
             assertThat(file.createNewFile()).isTrue();
@@ -1142,9 +1160,9 @@
 
     @Test
     public void testSystemGalleryAppHasFullAccessToImages() throws Exception {
-        final File otherAppImageFile = new File(DCIM_DIR, "other_" + IMAGE_FILE_NAME);
-        final File topLevelImageFile = new File(EXTERNAL_STORAGE_DIR, IMAGE_FILE_NAME);
-        final File imageInAnObviouslyWrongPlace = new File(MUSIC_DIR, IMAGE_FILE_NAME);
+        final File otherAppImageFile = new File(getDcimDir(), "other_" + IMAGE_FILE_NAME);
+        final File topLevelImageFile = new File(getExternalStorageDir(), IMAGE_FILE_NAME);
+        final File imageInAnObviouslyWrongPlace = new File(getMusicDir(), IMAGE_FILE_NAME);
 
         try {
             installApp(TEST_APP_A);
@@ -1182,9 +1200,9 @@
 
     @Test
     public void testSystemGalleryAppHasNoFullAccessToAudio() throws Exception {
-        final File otherAppAudioFile = new File(MUSIC_DIR, "other_" + AUDIO_FILE_NAME);
-        final File topLevelAudioFile = new File(EXTERNAL_STORAGE_DIR, AUDIO_FILE_NAME);
-        final File audioInAnObviouslyWrongPlace = new File(PICTURES_DIR, AUDIO_FILE_NAME);
+        final File otherAppAudioFile = new File(getMusicDir(), "other_" + AUDIO_FILE_NAME);
+        final File topLevelAudioFile = new File(getExternalStorageDir(), AUDIO_FILE_NAME);
+        final File audioInAnObviouslyWrongPlace = new File(getPicturesDir(), AUDIO_FILE_NAME);
 
         try {
             installApp(TEST_APP_A);
@@ -1217,11 +1235,11 @@
 
     @Test
     public void testSystemGalleryCanRenameImagesAndVideos() throws Exception {
-        final File otherAppVideoFile = new File(DCIM_DIR, "other_" + VIDEO_FILE_NAME);
-        final File imageFile = new File(PICTURES_DIR, IMAGE_FILE_NAME);
-        final File videoFile = new File(PICTURES_DIR, VIDEO_FILE_NAME);
-        final File topLevelVideoFile = new File(EXTERNAL_STORAGE_DIR, VIDEO_FILE_NAME);
-        final File musicFile = new File(MUSIC_DIR, AUDIO_FILE_NAME);
+        final File otherAppVideoFile = new File(getDcimDir(), "other_" + VIDEO_FILE_NAME);
+        final File imageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File videoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
+        final File topLevelVideoFile = new File(getExternalStorageDir(), VIDEO_FILE_NAME);
+        final File musicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
         try {
             installApp(TEST_APP_A);
             allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
@@ -1266,19 +1284,20 @@
      */
     @Test
     public void testRenameFile() throws Exception {
-        final File nonMediaDir = new File(DOWNLOAD_DIR, TEST_DIRECTORY_NAME);
-        final File pdfFile1 = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
+        final File downloadDir = getDownloadDir();
+        final File nonMediaDir = new File(downloadDir, TEST_DIRECTORY_NAME);
+        final File pdfFile1 = new File(downloadDir, NONMEDIA_FILE_NAME);
         final File pdfFile2 = new File(nonMediaDir, NONMEDIA_FILE_NAME);
-        final File videoFile1 = new File(DCIM_DIR, VIDEO_FILE_NAME);
-        final File videoFile2 = new File(MOVIES_DIR, VIDEO_FILE_NAME);
-        final File videoFile3 = new File(DOWNLOAD_DIR, VIDEO_FILE_NAME);
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
+        final File videoFile3 = new File(downloadDir, VIDEO_FILE_NAME);
 
         try {
             // Renaming non media file to media directory is not allowed.
             assertThat(pdfFile1.createNewFile()).isTrue();
-            assertCantRenameFile(pdfFile1, new File(DCIM_DIR, NONMEDIA_FILE_NAME));
-            assertCantRenameFile(pdfFile1, new File(MUSIC_DIR, NONMEDIA_FILE_NAME));
-            assertCantRenameFile(pdfFile1, new File(MOVIES_DIR, NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile1, new File(getDcimDir(), NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile1, new File(getMusicDir(), NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile1, new File(getMoviesDir(), NONMEDIA_FILE_NAME));
 
             // Renaming non media files to non media directories is allowed.
             if (!nonMediaDir.exists()) {
@@ -1307,13 +1326,13 @@
      */
     @Test
     public void testRenameFileType() throws Exception {
-        final File pdfFile = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
-        final File videoFile = new File(DCIM_DIR, VIDEO_FILE_NAME);
+        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        final File videoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
         try {
             assertThat(pdfFile.createNewFile()).isTrue();
             assertThat(videoFile.exists()).isFalse();
             // Moving pdfFile to DCIM directory is not allowed.
-            assertCantRenameFile(pdfFile, new File(DCIM_DIR, NONMEDIA_FILE_NAME));
+            assertCantRenameFile(pdfFile, new File(getDcimDir(), NONMEDIA_FILE_NAME));
             // However, moving pdfFile to DCIM directory with changing the mime type to video is
             // allowed.
             assertCanRenameFile(pdfFile, videoFile);
@@ -1332,8 +1351,8 @@
      */
     @Test
     public void testRenameAndReplaceFile() throws Exception {
-        final File videoFile1 = new File(DCIM_DIR, VIDEO_FILE_NAME);
-        final File videoFile2 = new File(MOVIES_DIR, VIDEO_FILE_NAME);
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
         final ContentResolver cr = getContentResolver();
         try {
             assertThat(videoFile1.createNewFile()).isTrue();
@@ -1360,8 +1379,8 @@
      */
     @Test
     public void testRenameFileNotOwned() throws Exception {
-        final File videoFile1 = new File(DCIM_DIR, VIDEO_FILE_NAME);
-        final File videoFile2 = new File(MOVIES_DIR, VIDEO_FILE_NAME);
+        final File videoFile1 = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File videoFile2 = new File(getMoviesDir(), VIDEO_FILE_NAME);
         try {
             installApp(TEST_APP_A);
             assertThat(createFileAs(TEST_APP_A, videoFile1.getAbsolutePath())).isTrue();
@@ -1385,16 +1404,18 @@
      */
     @Test
     public void testRenameDirectory() throws Exception {
+        final File dcimDir = getDcimDir();
+        final File downloadDir = getDownloadDir();
         final String nonMediaDirectoryName = TEST_DIRECTORY_NAME + "NonMedia";
-        final File nonMediaDirectory = new File(DOWNLOAD_DIR, nonMediaDirectoryName);
+        final File nonMediaDirectory = new File(downloadDir, nonMediaDirectoryName);
         final File pdfFile = new File(nonMediaDirectory, NONMEDIA_FILE_NAME);
 
         final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        final File mediaDirectory1 = new File(DCIM_DIR, mediaDirectoryName);
+        final File mediaDirectory1 = new File(dcimDir, mediaDirectoryName);
         final File videoFile1 = new File(mediaDirectory1, VIDEO_FILE_NAME);
-        final File mediaDirectory2 = new File(DOWNLOAD_DIR, mediaDirectoryName);
+        final File mediaDirectory2 = new File(downloadDir, mediaDirectoryName);
         final File videoFile2 = new File(mediaDirectory2, VIDEO_FILE_NAME);
-        final File mediaDirectory3 = new File(MOVIES_DIR, TEST_DIRECTORY_NAME);
+        final File mediaDirectory3 = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
         final File videoFile3 = new File(mediaDirectory3, VIDEO_FILE_NAME);
         final File mediaDirectory4 = new File(mediaDirectory3, mediaDirectoryName);
 
@@ -1404,7 +1425,7 @@
             }
             assertThat(pdfFile.createNewFile()).isTrue();
             // Move directory with pdf file to DCIM directory is not allowed.
-            assertThat(nonMediaDirectory.renameTo(new File(DCIM_DIR, nonMediaDirectoryName)))
+            assertThat(nonMediaDirectory.renameTo(new File(dcimDir, nonMediaDirectoryName)))
                     .isFalse();
 
             if (!mediaDirectory1.exists()) {
@@ -1412,9 +1433,9 @@
             }
             assertThat(videoFile1.createNewFile()).isTrue();
             // Renaming to and from default directories is not allowed.
-            assertThat(mediaDirectory1.renameTo(DCIM_DIR)).isFalse();
+            assertThat(mediaDirectory1.renameTo(dcimDir)).isFalse();
             // Moving top level default directories is not allowed.
-            assertCantRenameDirectory(DOWNLOAD_DIR, new File(DCIM_DIR, TEST_DIRECTORY_NAME), null);
+            assertCantRenameDirectory(downloadDir, new File(dcimDir, TEST_DIRECTORY_NAME), null);
 
             // Moving media directory to Download directory is allowed.
             assertCanRenameDirectory(mediaDirectory1, mediaDirectory2, new File[] {videoFile1},
@@ -1457,8 +1478,8 @@
     @Test
     public void testRenameDirectoryNotOwned() throws Exception {
         final String mediaDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        File mediaDirectory1 = new File(DCIM_DIR, mediaDirectoryName);
-        File mediaDirectory2 = new File(MOVIES_DIR, mediaDirectoryName);
+        File mediaDirectory1 = new File(getDcimDir(), mediaDirectoryName);
+        File mediaDirectory2 = new File(getMoviesDir(), mediaDirectoryName);
         File videoFile = new File(mediaDirectory1, VIDEO_FILE_NAME);
 
         try {
@@ -1486,8 +1507,8 @@
     @Test
     public void testRenameEmptyDirectory() throws Exception {
         final String emptyDirectoryName = TEST_DIRECTORY_NAME + "Media";
-        File emptyDirectoryOldPath = new File(DCIM_DIR, emptyDirectoryName);
-        File emptyDirectoryNewPath = new File(MOVIES_DIR, TEST_DIRECTORY_NAME);
+        File emptyDirectoryOldPath = new File(getDcimDir(), emptyDirectoryName);
+        File emptyDirectoryNewPath = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
         try {
             if (emptyDirectoryOldPath.exists()) {
                 executeShellCommand("rm -r " + emptyDirectoryOldPath.getPath());
@@ -1502,9 +1523,9 @@
 
     @Test
     public void testManageExternalStorageCanCreateFilesAnywhere() throws Exception {
-        final File topLevelPdf = new File(EXTERNAL_STORAGE_DIR, NONMEDIA_FILE_NAME);
-        final File musicFileInMovies = new File(MOVIES_DIR, AUDIO_FILE_NAME);
-        final File imageFileInDcim = new File(DCIM_DIR, IMAGE_FILE_NAME);
+        final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+        final File musicFileInMovies = new File(getMoviesDir(), AUDIO_FILE_NAME);
+        final File imageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
         try {
             allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
             // Nothing special about this, anyone can create an image file in DCIM
@@ -1524,7 +1545,7 @@
      */
     @Test
     public void testCanCreateHiddenFile() throws Exception {
-        final File hiddenImageFile = new File(DOWNLOAD_DIR, ".hiddenFile" + IMAGE_FILE_NAME);
+        final File hiddenImageFile = new File(getDownloadDir(), ".hiddenFile" + IMAGE_FILE_NAME);
         try {
             assertThat(hiddenImageFile.createNewFile()).isTrue();
             // Write to hidden file is allowed.
@@ -1535,7 +1556,7 @@
 
             assertNotMediaTypeImage(hiddenImageFile);
 
-            assertDirectoryContains(DOWNLOAD_DIR, hiddenImageFile);
+            assertDirectoryContains(getDownloadDir(), hiddenImageFile);
             assertThat(getFileRowIdFromDatabase(hiddenImageFile)).isNotEqualTo(-1);
 
             // We can delete hidden file
@@ -1552,9 +1573,9 @@
     @Test
     public void testCanRenameHiddenFile() throws Exception {
         final String hiddenFileName = ".hidden" + IMAGE_FILE_NAME;
-        final File hiddenImageFile1 = new File(DCIM_DIR, hiddenFileName);
-        final File hiddenImageFile2 = new File(DOWNLOAD_DIR, hiddenFileName);
-        final File imageFile = new File(DOWNLOAD_DIR, IMAGE_FILE_NAME);
+        final File hiddenImageFile1 = new File(getDcimDir(), hiddenFileName);
+        final File hiddenImageFile2 = new File(getDownloadDir(), hiddenFileName);
+        final File imageFile = new File(getDownloadDir(), IMAGE_FILE_NAME);
         try {
             assertThat(hiddenImageFile1.createNewFile()).isTrue();
             assertCanRenameFile(hiddenImageFile1, hiddenImageFile2);
@@ -1579,9 +1600,9 @@
      */
     @Test
     public void testHiddenDirectory() throws Exception {
-        final File hiddenDir = new File(DOWNLOAD_DIR, ".hidden" + TEST_DIRECTORY_NAME);
+        final File hiddenDir = new File(getDownloadDir(), ".hidden" + TEST_DIRECTORY_NAME);
         final File hiddenImageFile = new File(hiddenDir, IMAGE_FILE_NAME);
-        final File nonHiddenDir = new File(DOWNLOAD_DIR, TEST_DIRECTORY_NAME);
+        final File nonHiddenDir = new File(getDownloadDir(), TEST_DIRECTORY_NAME);
         final File imageFile = new File(nonHiddenDir, IMAGE_FILE_NAME);
         try {
             if (!hiddenDir.exists()) {
@@ -1612,7 +1633,7 @@
      */
     @Test
     public void testHiddenDirectory_nomedia() throws Exception {
-        final File directoryNoMedia = new File(DOWNLOAD_DIR, "nomedia" + TEST_DIRECTORY_NAME);
+        final File directoryNoMedia = new File(getDownloadDir(), "nomedia" + TEST_DIRECTORY_NAME);
         final File noMediaFile = new File(directoryNoMedia, ".nomedia");
         final File imageFile = new File(directoryNoMedia, IMAGE_FILE_NAME);
         final File videoFile = new File(directoryNoMedia, VIDEO_FILE_NAME);
@@ -1651,17 +1672,18 @@
      */
     @Test
     public void testListHiddenFile() throws Exception {
+        final File dcimDir = getDcimDir();
         final String hiddenImageFileName = ".hidden" + IMAGE_FILE_NAME;
-        final File hiddenImageFile = new File(DCIM_DIR, hiddenImageFileName);
+        final File hiddenImageFile = new File(dcimDir, hiddenImageFileName);
         try {
             assertThat(hiddenImageFile.createNewFile()).isTrue();
             assertNotMediaTypeImage(hiddenImageFile);
 
-            assertDirectoryContains(DCIM_DIR, hiddenImageFile);
+            assertDirectoryContains(dcimDir, hiddenImageFile);
 
             installApp(TEST_APP_A, true);
             // TestApp with read permissions can't see the hidden image file created by other app
-            assertThat(listAs(TEST_APP_A, DCIM_DIR.getAbsolutePath()))
+            assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
                     .doesNotContain(hiddenImageFileName);
 
             final int testAppUid =
@@ -1669,7 +1691,7 @@
             // FileManager can see the hidden image file created by other app
             try {
                 allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
-                assertThat(listAs(TEST_APP_A, DCIM_DIR.getAbsolutePath()))
+                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
                         .contains(hiddenImageFileName);
             } finally {
                 denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
@@ -1678,7 +1700,7 @@
             // Gallery can not see the hidden image file created by other app
             try {
                 allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
-                assertThat(listAs(TEST_APP_A, DCIM_DIR.getAbsolutePath()))
+                assertThat(listAs(TEST_APP_A, dcimDir.getAbsolutePath()))
                         .doesNotContain(hiddenImageFileName);
             } finally {
                 denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
@@ -1690,10 +1712,153 @@
     }
 
     @Test
+    public void testOpenPendingAndTrashed() throws Exception {
+        final File pendingImageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File trashedVideoFile = new File(getPicturesDir(), VIDEO_FILE_NAME);
+        final File pendingPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+        final File trashedPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        Uri pendingImgaeFileUri = null;
+        Uri trashedVideoFileUri = null;
+        Uri pendingPdfFileUri = null;
+        Uri trashedPdfFileUri = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            pendingImgaeFileUri = createPendingFile(pendingImageFile);
+            assertOpenPendingOrTrashed(pendingImgaeFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+
+            pendingPdfFileUri = createPendingFile(pendingPdfFile);
+            assertOpenPendingOrTrashed(pendingPdfFileUri, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+            trashedVideoFileUri = createTrashedFile(trashedVideoFile);
+            assertOpenPendingOrTrashed(trashedVideoFileUri, TEST_APP_A, /*isImageOrVideo*/ true);
+
+            trashedPdfFileUri = createTrashedFile(trashedPdfFile);
+            assertOpenPendingOrTrashed(trashedPdfFileUri, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+        } finally {
+            deleteFiles(pendingImageFile, pendingImageFile, trashedVideoFile,
+                    trashedPdfFile);
+            deleteWithMediaProviderNoThrow(pendingImgaeFileUri, trashedVideoFileUri,
+                    pendingPdfFileUri, trashedPdfFileUri);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testListPendingAndTrashed() throws Exception {
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File pdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        Uri imageFileUri = null;
+        Uri pdfFileUri = null;
+        try {
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            imageFileUri = createPendingFile(imageFile);
+            // Check that only owner package, file manager and system gallery can list pending image
+            // file.
+            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+                    /*isImageOrVideo*/ true);
+
+            trashFile(imageFileUri);
+            // Check that only owner package, file manager and system gallery can list trashed image
+            // file.
+            assertListPendingOrTrashed(imageFileUri, imageFile, TEST_APP_A,
+                    /*isImageOrVideo*/ true);
+
+            pdfFileUri = createPendingFile(pdfFile);
+            // Check that only owner package, file manager can list pending non media file.
+            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+
+            trashFile(pdfFileUri);
+            // Check that only owner package, file manager can list trashed non media file.
+            assertListPendingOrTrashed(pdfFileUri, pdfFile, TEST_APP_A,
+                    /*isImageOrVideo*/ false);
+        } finally {
+            deleteWithMediaProviderNoThrow(imageFileUri, pdfFileUri);
+            deleteFiles(imageFile, pdfFile);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
+    public void testDeletePendingAndTrashed() throws Exception {
+        final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+        final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+        final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+        // Actual path of the file gets rewritten for pending and trashed files.
+        String pendingVideoFilePath = null;
+        String trashedImageFilePath = null;
+        String pendingPdfFilePath = null;
+        String trashedPdfFilePath = null;
+        try {
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            // App can delete its own pending and trashed file.
+            assertCanDeletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+                    trashedPdfFilePath);
+
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            installAppWithStoragePermissions(TEST_APP_A);
+
+            // App can't delete other app's pending and trashed file.
+            assertCantDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+                    pendingPdfFilePath, trashedPdfFilePath);
+
+            final int testAppUid =
+                    getContext().getPackageManager().getPackageUid(TEST_APP_A.getPackageName(), 0);
+            try {
+                allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+                // File Manager can delete any pending and trashed file
+                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath,
+                        pendingPdfFilePath, trashedPdfFilePath);
+            } finally {
+                denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            }
+
+            pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+            trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+            pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+            trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
+
+            try {
+                allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+                // System Gallery can delete any pending and trashed image or video file.
+                assertTrue(isMediaTypeImageOrVideo(new File(pendingVideoFilePath)));
+                assertTrue(isMediaTypeImageOrVideo(new File(trashedImageFilePath)));
+                assertCanDeletePathsAs(TEST_APP_A, pendingVideoFilePath, trashedImageFilePath);
+
+                // System Gallery can't delete other app's pending and trashed pdf file.
+                assertFalse(isMediaTypeImageOrVideo(new File(pendingPdfFilePath)));
+                assertFalse(isMediaTypeImageOrVideo(new File(trashedPdfFilePath)));
+                assertCantDeletePathsAs(TEST_APP_A, pendingPdfFilePath, trashedPdfFilePath);
+            } finally {
+                denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            }
+        } finally {
+            deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+                    trashedPdfFilePath);
+            deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
+            uninstallAppNoThrow(TEST_APP_A);
+        }
+    }
+
+    @Test
     public void testManageExternalStorageCanDeleteOtherAppsContents() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImage = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(MUSIC_DIR, "other" + AUDIO_FILE_NAME);
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImage = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
         try {
             installApp(TEST_APP_A);
 
@@ -1725,10 +1890,11 @@
     public void testAccess_file() throws Exception {
         pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
 
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other-" + NONMEDIA_FILE_NAME);
-        final File otherAppImage = new File(DCIM_DIR, "other-" + IMAGE_FILE_NAME);
-        final File myAppPdf = new File(DOWNLOAD_DIR, "my-" + NONMEDIA_FILE_NAME);
-        final File doesntExistPdf = new File(DOWNLOAD_DIR, "nada-" + NONMEDIA_FILE_NAME);
+        final File downloadDir = getDownloadDir();
+        final File otherAppPdf = new File(downloadDir, "other-" + NONMEDIA_FILE_NAME);
+        final File otherAppImage = new File(getDcimDir(), "other-" + IMAGE_FILE_NAME);
+        final File myAppPdf = new File(downloadDir, "my-" + NONMEDIA_FILE_NAME);
+        final File doesntExistPdf = new File(downloadDir, "nada-" + NONMEDIA_FILE_NAME);
 
         try {
             installApp(TEST_APP_A);
@@ -1761,7 +1927,7 @@
             installApp(TEST_APP_A);
 
             // Let app A create a file in its data dir
-            final File otherAppExternalDataDir = new File(EXTERNAL_FILES_DIR.getPath().replace(
+            final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace(
                     THIS_PACKAGE_NAME, TEST_APP_A.getPackageName()));
             final File otherAppExternalDataSubDir = new File(otherAppExternalDataDir, "subdir");
             final File otherAppExternalDataFile = new File(otherAppExternalDataSubDir, "abc.jpg");
@@ -1786,13 +1952,13 @@
             //
             //            // We can read and write our own app dir, but app A cannot.
             //            assertThat(canReadAndWriteAs(TEST_APP_A,
-            //                    EXTERNAL_FILES_DIR.getAbsolutePath())).isFalse();
-            assertAccess(EXTERNAL_FILES_DIR, true, true, true);
+            //                    getExternalFilesDir().getAbsolutePath())).isFalse();
+            assertAccess(getExternalFilesDir(), true, true, true);
 
-            assertDirectoryAccess(DCIM_DIR, /* exists */ true);
-            assertDirectoryAccess(EXTERNAL_STORAGE_DIR, true);
-            assertDirectoryAccess(new File(EXTERNAL_STORAGE_DIR, "Android"), true);
-            assertDirectoryAccess(new File(EXTERNAL_STORAGE_DIR, "doesnt/exist"), false);
+            assertDirectoryAccess(getDcimDir(), /* exists */ true);
+            assertDirectoryAccess(getExternalStorageDir(), true);
+            assertDirectoryAccess(new File(getExternalStorageDir(), "Android"), true);
+            assertDirectoryAccess(new File(getExternalStorageDir(), "doesnt/exist"), false);
         } finally {
             uninstallApp(TEST_APP_A); // Uninstalling deletes external app dirs
         }
@@ -1800,11 +1966,11 @@
 
     @Test
     public void testManageExternalStorageCanRenameOtherAppsContents() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File pdf = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
-        final File pdfInObviouslyWrongPlace = new File(PICTURES_DIR, NONMEDIA_FILE_NAME);
-        final File topLevelPdf = new File(EXTERNAL_STORAGE_DIR, NONMEDIA_FILE_NAME);
-        final File musicFile = new File(MUSIC_DIR, AUDIO_FILE_NAME);
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File pdf = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+        final File pdfInObviouslyWrongPlace = new File(getPicturesDir(), NONMEDIA_FILE_NAME);
+        final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+        final File musicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
         try {
             installApp(TEST_APP_A);
 
@@ -1847,24 +2013,26 @@
 
     @Test
     public void testCanCreateDefaultDirectory() throws Exception {
+        final File podcastsDir = getPodcastsDir();
         try {
-            if (PODCASTS_DIR.exists()) {
+            if (podcastsDir.exists()) {
                 // Apps can't delete top level directories, not even default directories, so we let
                 // shell do the deed for us.
-                executeShellCommand("rm -r " + PODCASTS_DIR);
+                executeShellCommand("rm -r " + podcastsDir);
             }
-            assertThat(PODCASTS_DIR.mkdir()).isTrue();
+            assertThat(podcastsDir.mkdir()).isTrue();
         } finally {
-            executeShellCommand("mkdir " + PODCASTS_DIR);
+            executeShellCommand("mkdir " + podcastsDir);
         }
     }
 
     @Test
     public void testManageExternalStorageReaddir() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(MUSIC_DIR, "other" + AUDIO_FILE_NAME);
-        final File otherTopLevelFile = new File(EXTERNAL_STORAGE_DIR, "other" + NONMEDIA_FILE_NAME);
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherTopLevelFile = new File(getExternalStorageDir(),
+                "other" + NONMEDIA_FILE_NAME);
         try {
             installApp(TEST_APP_A);
             assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
@@ -1877,10 +2045,10 @@
             assertDirectoryContains(otherAppImg.getParentFile(), otherAppImg);
             assertDirectoryContains(otherAppMusic.getParentFile(), otherAppMusic);
             // We can list top level files
-            assertDirectoryContains(EXTERNAL_STORAGE_DIR, otherTopLevelFile);
+            assertDirectoryContains(getExternalStorageDir(), otherTopLevelFile);
 
             // We can also list all top level directories
-            assertDirectoryContains(EXTERNAL_STORAGE_DIR, DEFAULT_TOP_LEVEL_DIRS);
+            assertDirectoryContains(getExternalStorageDir(), getDefaultTopLevelDirs());
         } finally {
             denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
             executeShellCommand("rm " + otherTopLevelFile);
@@ -1891,10 +2059,10 @@
 
     @Test
     public void testManageExternalStorageQueryOtherAppsFile() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(MUSIC_DIR, "other" + AUDIO_FILE_NAME);
-        final File otherHiddenFile = new File(PICTURES_DIR, ".otherHiddenFile.jpg");
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
         try {
             installApp(TEST_APP_A);
             assertCreateFilesAs(
@@ -1917,10 +2085,10 @@
 
     @Test
     public void testQueryOtherAppsFiles() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(MUSIC_DIR, "other" + AUDIO_FILE_NAME);
-        final File otherHiddenFile = new File(PICTURES_DIR, ".otherHiddenFile.jpg");
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
         try {
             installApp(TEST_APP_A);
             assertCreateFilesAs(
@@ -1940,10 +2108,10 @@
 
     @Test
     public void testSystemGalleryQueryOtherAppsFiles() throws Exception {
-        final File otherAppPdf = new File(DOWNLOAD_DIR, "other" + NONMEDIA_FILE_NAME);
-        final File otherAppImg = new File(DCIM_DIR, "other" + IMAGE_FILE_NAME);
-        final File otherAppMusic = new File(MUSIC_DIR, "other" + AUDIO_FILE_NAME);
-        final File otherHiddenFile = new File(PICTURES_DIR, ".otherHiddenFile.jpg");
+        final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+        final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+        final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+        final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
         try {
             installApp(TEST_APP_A);
             assertCreateFilesAs(
@@ -1972,9 +2140,9 @@
      */
     @Test
     public void testSystemGalleryCanRenameImageAndVideoDirs() throws Exception {
-        final File dirInDcim = new File(DCIM_DIR, TEST_DIRECTORY_NAME);
-        final File dirInPictures = new File(PICTURES_DIR, TEST_DIRECTORY_NAME);
-        final File dirInPodcasts = new File(PODCASTS_DIR, TEST_DIRECTORY_NAME);
+        final File dirInDcim = new File(getDcimDir(), TEST_DIRECTORY_NAME);
+        final File dirInPictures = new File(getPicturesDir(), TEST_DIRECTORY_NAME);
+        final File dirInPodcasts = new File(getPodcastsDir(), TEST_DIRECTORY_NAME);
         final File otherAppImageFile1 = new File(dirInDcim, "other_" + IMAGE_FILE_NAME);
         final File otherAppVideoFile1 = new File(dirInDcim, "other_" + VIDEO_FILE_NAME);
         final File otherAppPdfFile1 = new File(dirInDcim, "other_" + NONMEDIA_FILE_NAME);
@@ -2021,7 +2189,7 @@
      */
     @Test
     public void testCreateCanRestoreDeletedRowId() throws Exception {
-        final File imageFile = new File(DCIM_DIR, IMAGE_FILE_NAME);
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
         final ContentResolver cr = getContentResolver();
 
         try {
@@ -2057,8 +2225,8 @@
      */
     @Test
     public void testRenameCanRestoreDeletedRowId() throws Exception {
-        final File imageFile = new File(DCIM_DIR, IMAGE_FILE_NAME);
-        final File temporaryFile = new File(DOWNLOAD_DIR, IMAGE_FILE_NAME + "_.tmp");
+        final File imageFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+        final File temporaryFile = new File(getDownloadDir(), IMAGE_FILE_NAME + "_.tmp");
         final ContentResolver cr = getContentResolver();
 
         try {
@@ -2083,8 +2251,8 @@
 
     @Test
     public void testCantCreateOrRenameFileWithInvalidName() throws Exception {
-        File invalidFile = new File(DOWNLOAD_DIR, "<>");
-        File validFile = new File(DOWNLOAD_DIR, NONMEDIA_FILE_NAME);
+        File invalidFile = new File(getDownloadDir(), "<>");
+        File validFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
         try {
             assertThrows(IOException.class, "Operation not permitted",
                     () -> { invalidFile.createNewFile(); });
@@ -2098,6 +2266,148 @@
         }
     }
 
+    /**
+     * Checks restrictions for opening pending and trashed files by different apps. Assumes that
+     * given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
+     * method doesn't uninstall given {@code testApp} at the end.
+     */
+    private void assertOpenPendingOrTrashed(Uri uri, TestApp testApp, boolean isImageOrVideo)
+            throws Exception {
+        final File pendingOrTrashedFile = new File(getFilePathFromUri(uri));
+
+        // App can open its pending or trashed file for read or write
+        assertTrue(canOpen(pendingOrTrashedFile, /*forWrite*/ false));
+        assertTrue(canOpen(pendingOrTrashedFile, /*forWrite*/ true));
+
+        // App with READ_EXTERNAL_STORAGE can't open other app's pending or trashed file for read or
+        // write
+        assertFalse(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+        assertFalse(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+
+        final int testAppUid =
+                getContext().getPackageManager().getPackageUid(testApp.getPackageName(), 0);
+        try {
+            allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            // File Manager can open any pending or trashed file for read or write
+            assertTrue(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+            assertTrue(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+        } finally {
+            denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+        }
+
+        try {
+            allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            if (isImageOrVideo) {
+                // System Gallery can open any pending or trashed image/video file for read or write
+                assertTrue(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertTrue(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+                assertTrue(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+            } else {
+                // System Gallery can't open other app's pending or trashed non-media file for read
+                // or write
+                assertFalse(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertFalse(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ false));
+                assertFalse(openFileAs(testApp, pendingOrTrashedFile, /*forWrite*/ true));
+            }
+        } finally {
+            denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    /**
+     * Checks restrictions for listing pending and trashed files by different apps. Assumes that
+     * given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
+     * method doesn't uninstall given {@code testApp} at the end.
+     */
+    private void assertListPendingOrTrashed(Uri uri, File file, TestApp testApp,
+            boolean isImageOrVideo) throws Exception {
+        final String parentDirPath = file.getParent();
+        assertTrue(new File(parentDirPath).isDirectory());
+
+        final List<String> listedFileNames = Arrays.asList(new File(parentDirPath).list());
+        assertThat(listedFileNames).doesNotContain(file);
+
+        final File pendingOrTrashedFile = new File(getFilePathFromUri(uri));
+
+        assertThat(listedFileNames).contains(pendingOrTrashedFile.getName());
+
+        // App with READ_EXTERNAL_STORAGE can't see other app's pending or trashed file.
+        assertThat(listAs(testApp, parentDirPath)).doesNotContain(pendingOrTrashedFile.getName());
+
+        final int testAppUid =
+                getContext().getPackageManager().getPackageUid(testApp.getPackageName(), 0);
+        try {
+            allowAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+            // File Manager can see any pending or trashed file.
+            assertThat(listAs(testApp, parentDirPath)).contains(pendingOrTrashedFile.getName());
+        } finally {
+            denyAppOpsToUid(testAppUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
+        }
+
+        try {
+            allowAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+            if (isImageOrVideo) {
+                // System Gallery can see any pending or trashed image/video file.
+                assertTrue(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertThat(listAs(testApp, parentDirPath)).contains(pendingOrTrashedFile.getName());
+            } else {
+                // System Gallery can't see other app's pending or trashed non media file.
+                assertFalse(isMediaTypeImageOrVideo(pendingOrTrashedFile));
+                assertThat(listAs(testApp, parentDirPath))
+                        .doesNotContain(pendingOrTrashedFile.getName());
+            }
+        } finally {
+            denyAppOpsToUid(testAppUid, SYSTEM_GALERY_APPOPS);
+        }
+    }
+
+    private Uri createPendingFile(File pendingFile) throws Exception {
+        assertTrue(pendingFile.createNewFile());
+
+        final ContentResolver cr = getContentResolver();
+        final Uri trashedFileUri = MediaStore.scanFile(cr, pendingFile);
+        assertNotNull(trashedFileUri);
+
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_PENDING, 1);
+        assertEquals(1, cr.update(trashedFileUri, values, Bundle.EMPTY));
+
+        return trashedFileUri;
+    }
+
+    private Uri createTrashedFile(File trashedFile) throws Exception {
+        assertTrue(trashedFile.createNewFile());
+
+        final ContentResolver cr = getContentResolver();
+        final Uri trashedFileUri = MediaStore.scanFile(cr, trashedFile);
+        assertNotNull(trashedFileUri);
+
+        trashFile(trashedFileUri);
+        return trashedFileUri;
+    }
+
+    private void trashFile(Uri uri) throws Exception {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 1);
+        assertEquals(1, getContentResolver().update(uri, values, Bundle.EMPTY));
+    }
+
+    /**
+     * Gets file path corresponding to the db row pointed by {@code uri}. If {@code uri} points to
+     * multiple db rows, file path is extracted from the first db row of the database query result.
+     */
+    private String getFilePathFromUri(Uri uri) {
+        final String[] projection = new String[] {MediaColumns.DATA};
+        try (Cursor c = getContentResolver().query(uri, projection, null, null)) {
+            assertTrue(c.moveToFirst());
+            return c.getString(0);
+        }
+    }
+
+    private boolean isMediaTypeImageOrVideo(File file) {
+        return queryImageFile(file).getCount() == 1 || queryVideoFile(file).getCount() == 1;
+    }
+
     private static void assertIsMediaTypeImage(File file) {
         final Cursor c = queryImageFile(file);
         assertEquals(1, c.getCount());
@@ -2121,6 +2431,39 @@
             deleteFileAs(testApp, file.getPath());
         }
     }
+    private static void assertCanDeletePathsAs(TestApp testApp, String... filePaths)
+            throws Exception {
+        for (String path: filePaths) {
+            assertTrue(deleteFileAs(testApp, path));
+        }
+    }
+
+    private static void assertCantDeletePathsAs(TestApp testApp, String... filePaths)
+            throws Exception {
+        for (String path: filePaths) {
+            assertFalse(deleteFileAs(testApp, path));
+        }
+    }
+
+    private void deleteFiles(File... files) {
+        for (File file: files) {
+            if (file == null) continue;
+            file.delete();
+        }
+    }
+
+    private void deletePaths(String... paths) {
+        for (String path: paths) {
+            if (path == null) continue;
+            new File(path).delete();
+        }
+    }
+
+    private static void assertCanDeletePaths(String... filePaths) {
+        for (String filePath : filePaths) {
+            assertTrue(new File(filePath).delete());
+        }
+    }
 
     /**
      * For possible values of {@code mode}, look at {@link android.content.ContentProvider#openFile}
@@ -2210,8 +2553,8 @@
 
     private static void assertDirectoryAccess(File dir, boolean exists) throws Exception {
         // This util does not handle app data directories.
-        assumeFalse(dir.getAbsolutePath().startsWith(ANDROID_DIR.getAbsolutePath())
-                && !dir.equals(ANDROID_DIR));
+        assumeFalse(dir.getAbsolutePath().startsWith(getAndroidDir().getAbsolutePath())
+                && !dir.equals(getAndroidDir()));
         assertThat(dir.isDirectory()).isEqualTo(exists);
         // For non-app data directories, exists => canRead() and canWrite().
         assertAccess(dir, exists, exists, exists);
diff --git a/hostsidetests/security/src/android/security/cts/PerfEventParanoidTest.java b/hostsidetests/security/src/android/security/cts/PerfEventParanoidTest.java
index 7ede5a7..e285d99 100644
--- a/hostsidetests/security/src/android/security/cts/PerfEventParanoidTest.java
+++ b/hostsidetests/security/src/android/security/cts/PerfEventParanoidTest.java
@@ -15,43 +15,63 @@
  */
 package android.security.cts;
 
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.PropertyUtil;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.DeviceTestCase;
 
+/**
+ * Tests permission/security controls of the perf_event_open syscall.
+ */
 public class PerfEventParanoidTest extends DeviceTestCase {
 
-   /**
-    * a reference to the device under test.
-    */
+    // A reference to the device under test.
     private ITestDevice mDevice;
 
     private static final String PERF_EVENT_PARANOID_PATH = "/proc/sys/kernel/perf_event_paranoid";
     private static final String PERF_EVENT_LSM_SYSPROP = "sys.init.perf_lsm_hooks";
 
+    private static final int ANDROID_R_API_LEVEL = 30;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mDevice = getDevice();
     }
 
+    @CddTest(requirement="9.7")
     public void testPerfEventRestricted() throws DeviceNotAvailableException {
-      // Property set to "1" if init detected that the kernel has the perf_event_open SELinux hooks,
-      // otherwise left unset.
-      long lsmHookPropValue = mDevice.getIntProperty(PERF_EVENT_LSM_SYSPROP, 0);
+        // Property set to "1" if init detected that the kernel has the perf_event_open SELinux
+        // hooks, otherwise left unset.
+        long lsmHookPropValue = mDevice.getIntProperty(PERF_EVENT_LSM_SYSPROP, 0);
 
-      String paranoidCmd = "cat " + PERF_EVENT_PARANOID_PATH;
-      String paranoidOut = mDevice.executeShellCommand(paranoidCmd);
+        // Contents of the perf_event_paranoid sysctl procfs file.
+        String paranoidCmd = "cat " + PERF_EVENT_PARANOID_PATH;
+        String paranoidOut = mDevice.executeShellCommand(paranoidCmd);
 
-      if (lsmHookPropValue != 1 && !paranoidOut.equals("3\n")) {
-        fail("\nDevice required to have either:\n"
-            + " (a) SELinux hooks for the perf_event_open(2) syscall\n"
-            + " (b) /proc/sys/kernel/perf_event_paranoid=3\n"
-            + "For (a), apply the relevant patch for your kernel located here:\n"
-            + "https://android-review.googlesource.com/q/hashtag:perf_event_lsm\n"
-            + "For (b), apply the relevant patch for your kernel located here:\n"
-            + "https://android-review.googlesource.com/#/q/topic:CONFIG_SECURITY_PERF_EVENTS_RESTRICT\n"
-            + "Device values: SELinux=" + lsmHookPropValue + ", paranoid=" + paranoidOut);
-      }
+        if (PropertyUtil.getFirstApiLevel(mDevice) >= ANDROID_R_API_LEVEL) {
+            // On devices launching with Android R or above, the kernel must have the LSM hooks.
+            if (lsmHookPropValue != 1) {
+                fail("\nDevices launching with Android R or above are required to have SELinux "
+                        + "hooks for the perf_event_open(2) syscall.\n"
+                        + "Please apply the relevant patch for your kernel located here:\n"
+                        + "https://android-review.googlesource.com/q/hashtag:perf_event_lsm");
+            }
+        } else {
+            // Devices upgrading to Android R can have either the LSM hooks, or
+            // default to perf_event_paranoid=3.
+            if (lsmHookPropValue != 1 && !paranoidOut.equals("3\n")) {
+                fail("\nDevice required to have either:\n"
+                        + " (a) SELinux hooks for the perf_event_open(2) syscall\n"
+                        + " (b) /proc/sys/kernel/perf_event_paranoid=3\n"
+                        + "For (a), apply the relevant patch for your kernel located here:\n"
+                        + "https://android-review.googlesource.com/q/hashtag:perf_event_lsm\n"
+                        + "For (b), apply the relevant patch for your kernel located here:\n"
+                        + "https://android-review.googlesource.com/#/q/topic:CONFIG_SECURITY_PERF_EVENTS_RESTRICT\n"
+                        + "Device values: SELinux=" + lsmHookPropValue
+                        + ", paranoid=" + paranoidOut);
+            }
+        }
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 2d980aa..1c68c4a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -1975,7 +1975,6 @@
 
     public void testMobileBytesTransfer() throws Throwable {
         final int appUid = getUid();
-        final boolean subtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
 
         // Verify MobileBytesTransfer, passing a ThrowingPredicate that verifies contents of
         // corresponding atom type to prevent code duplication. The passed predicate returns
@@ -1994,7 +1993,6 @@
 
     public void testMobileBytesTransferByFgBg() throws Throwable {
         final int appUid = getUid();
-        final boolean subtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
 
         doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_BY_FG_BG_FIELD_NUMBER, (atom) -> {
             final AtomsProto.MobileBytesTransferByFgBg data =
@@ -2011,6 +2009,34 @@
         });
     }
 
+    public void testDataUsageBytesTransfer() throws Throwable {
+        final boolean subtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
+
+        doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
+            final AtomsProto.DataUsageBytesTransfer data =
+                    ((Atom) atom).getDataUsageBytesTransfer();
+            assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
+                    data.getRxPackets(), data.getTxPackets());
+            // TODO: verify the RAT type field with the value gotten from device.
+            if (subtypeCombined) {
+                assertThat(data.getRatType()).isEqualTo(NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
+            } else {
+                assertThat(data.getRatType()).isGreaterThan(
+                        NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
+            }
+            // Foreground state cannot be judged since foreground activity that launched
+            // while screen off (PROCESS_STATE_TOP_SLEEPING) will be treated as background
+            // in NetworkPolicyManagerService.
+
+            // Assert that subscription info is valid.
+            assertThat(data.getSimMcc()).matches("^\\d{3}$");
+            assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
+            assertThat(data.getCarrierId()).isNotEqualTo(-1); // TelephonyManager#UNKNOWN_CARRIER_ID
+
+            return true;
+        });
+    }
+
     public void testIsolatedToHostUidMapping() throws Exception {
         createAndUploadConfig(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, /*useAttribution=*/false);
         Thread.sleep(WAIT_TIME_SHORT);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 2b2eee7..475feda 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -37,6 +37,7 @@
 import com.android.os.AtomsProto.Atom;
 import com.android.os.AtomsProto.SystemElapsedRealtime;
 import com.android.os.StatsLog.StatsLogReport;
+import com.android.os.StatsLog.StatsLogReport.BucketDropReason;
 import com.android.os.StatsLog.ValueBucketInfo;
 import com.android.os.StatsLog.ValueMetricData;
 
@@ -330,7 +331,12 @@
     StatsLogReport metricReport = getStatsLogReport();
     LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
     assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
-    assertThat(metricReport.hasValueMetrics()).isFalse();
+    assertThat(metricReport.getValueMetrics().getDataList()).isEmpty();
+    // Bucket is skipped because metric is not activated.
+    assertThat(metricReport.getValueMetrics().getSkippedList()).isNotEmpty();
+    assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEventList()).isNotEmpty();
+    assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEvent(0).getDropReason())
+            .isEqualTo(BucketDropReason.NO_DATA);
   }
 
     public void testValueMetricWithConditionAndActivation() throws Exception {
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index ba06090..4a3570b 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -22,6 +22,7 @@
 import static com.android.utils.blob.Utils.assertNoLeasedBlobs;
 import static com.android.utils.blob.Utils.releaseLease;
 import static com.android.utils.blob.Utils.TAG;
+import static com.android.utils.blob.Utils.runShellCmd;
 import static com.android.utils.blob.Utils.triggerIdleMaintenance;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -993,6 +994,57 @@
     }
 
     @Test
+    public void testAccessExpiredBlob() throws Exception {
+        final long expiryDurationMs = TimeUnit.SECONDS.toMillis(6);
+        final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+                .setExpiryDurationMs(expiryDurationMs)
+                .build();
+        blobData.prepare();
+
+        final long startTimeMs = System.currentTimeMillis();
+        final long blobId = commitBlob(blobData);
+        assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId)).isEqualTo("1");
+        final long commitDurationMs = System.currentTimeMillis() - startTimeMs;
+
+        SystemClock.sleep(Math.abs(expiryDurationMs - commitDurationMs));
+
+        assertThrows(SecurityException.class,
+                () -> mBlobStoreManager.openBlob(blobData.getBlobHandle()));
+        assertThrows(SecurityException.class,
+                () -> mBlobStoreManager.acquireLease(blobData.getBlobHandle(),
+                        R.string.test_desc));
+
+        triggerIdleMaintenance();
+        assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId)).isEqualTo("0");
+    }
+
+    @Test
+    public void testAccessExpiredBlob_withLeaseAcquired() throws Exception {
+        final long expiryDurationMs = TimeUnit.SECONDS.toMillis(6);
+        final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+                .setExpiryDurationMs(expiryDurationMs)
+                .build();
+        blobData.prepare();
+
+        final long startTimeMs = System.currentTimeMillis();
+        final long blobId = commitBlob(blobData);
+        assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId)).isEqualTo("1");
+        final long commitDurationMs = System.currentTimeMillis() - startTimeMs;
+        acquireLease(mContext, blobData.getBlobHandle(), R.string.test_desc);
+
+        SystemClock.sleep(Math.abs(expiryDurationMs - commitDurationMs));
+
+        assertThrows(SecurityException.class,
+                () -> mBlobStoreManager.openBlob(blobData.getBlobHandle()));
+        assertThrows(SecurityException.class,
+                () -> mBlobStoreManager.acquireLease(blobData.getBlobHandle(),
+                        R.string.test_desc));
+
+        triggerIdleMaintenance();
+        assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId)).isEqualTo("0");
+    }
+
+    @Test
     public void testCommitBlobAfterIdleMaintenance() throws Exception {
         final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
         blobData.prepare();
@@ -1008,7 +1060,7 @@
         SystemClock.sleep(waitDurationMs);
 
         // Trigger idle maintenance which takes of deleting expired sessions.
-        triggerIdleMaintenance(InstrumentationRegistry.getInstrumentation());
+        triggerIdleMaintenance();
 
         try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
             blobData.writeToSession(session, partialFileSize,
@@ -1029,6 +1081,24 @@
             final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
             assertThat(sessionId).isGreaterThan(0L);
 
+            SystemClock.sleep(waitDurationMs);
+
+            // Trigger idle maintenance which takes of deleting expired sessions.
+            triggerIdleMaintenance();
+
+            assertThrows(SecurityException.class, () -> mBlobStoreManager.openSession(sessionId));
+        }, Pair.create(KEY_SESSION_EXPIRY_TIMEOUT_MS, String.valueOf(waitDurationMs)));
+    }
+
+    @Test
+    public void testExpiredSessionsDeleted_withPartialData() throws Exception {
+        final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+        blobData.prepare();
+        final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
+        runWithKeyValues(() -> {
+            final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+            assertThat(sessionId).isGreaterThan(0L);
+
             try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
                 blobData.writeToSession(session, 0, 100);
             }
@@ -1036,7 +1106,7 @@
             SystemClock.sleep(waitDurationMs);
 
             // Trigger idle maintenance which takes of deleting expired sessions.
-            triggerIdleMaintenance(InstrumentationRegistry.getInstrumentation());
+            triggerIdleMaintenance();
 
             assertThrows(SecurityException.class, () -> mBlobStoreManager.openSession(sessionId));
         }, Pair.create(KEY_SESSION_EXPIRY_TIMEOUT_MS, String.valueOf(waitDurationMs)));
@@ -1076,11 +1146,11 @@
         }
     }
 
-    private void commitBlob(DummyBlobData blobData) throws Exception {
-        commitBlob(blobData, null);
+    private long commitBlob(DummyBlobData blobData) throws Exception {
+        return commitBlob(blobData, null);
     }
 
-    private void commitBlob(DummyBlobData blobData,
+    private long commitBlob(DummyBlobData blobData,
             AccessModifier accessModifier) throws Exception {
         final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
         assertThat(sessionId).isGreaterThan(0L);
@@ -1095,6 +1165,7 @@
             assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
                     .isEqualTo(0);
         }
+        return sessionId;
     }
 
     private interface AccessModifier {
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index cd52ef5..d4d5459 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application>
 
@@ -135,6 +136,7 @@
         </activity>
         <activity android:name=".SimpleAfterLoginActivity" />
         <activity android:name=".SimpleBeforeLoginActivity" />
+        <activity android:name=".NonAutofillableActivity" />
 
         <receiver android:name=".SelfDestructReceiver"
             android:exported="true"
diff --git a/tests/autofillservice/res/layout/non_autofillable_activity.xml b/tests/autofillservice/res/layout/non_autofillable_activity.xml
new file mode 100644
index 0000000..b0d4f4d
--- /dev/null
+++ b/tests/autofillservice/res/layout/non_autofillable_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 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:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:focusable="true"
+              android:focusableInTouchMode="true"
+              android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/username_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <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:importantForAutofill="no"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 533b8df..b3315fd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -18,6 +18,7 @@
 
 import static android.autofillservice.cts.UiBot.PORTRAIT;
 import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
+import static android.provider.Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
 import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
@@ -37,6 +38,7 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -45,6 +47,7 @@
 import android.icu.util.Calendar;
 import android.os.Bundle;
 import android.os.Environment;
+import android.provider.Settings;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
@@ -1539,12 +1542,18 @@
     private static InlinePresentation createInlinePresentation(@NonNull String message,
             @NonNull PendingIntent attribution, boolean pinned) {
         return new InlinePresentation(
-                InlineSuggestionUi.newContentBuilder().setAttribution(attribution)
+                InlineSuggestionUi.newContentBuilder(attribution)
                         .setTitle(message).build().getSlice(),
                 new InlinePresentationSpec.Builder(new Size(100, 100), new Size(400, 100))
                         .build(), /* pinned= */ pinned);
     }
 
+    public static void mockSwitchInputMethod(@NonNull Context context) throws Exception {
+        final ContentResolver cr = context.getContentResolver();
+        final int subtype = Settings.Secure.getInt(cr, SELECTED_INPUT_METHOD_SUBTYPE);
+        Settings.Secure.putInt(cr, SELECTED_INPUT_METHOD_SUBTYPE, subtype);
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contain static methods only");
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 6baa5a1..255001a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -2858,4 +2858,38 @@
         // Verify auto-fill has been triggered.
         mUiBot.assertDatasetsContains("The Dude");
     }
+
+    @Test
+    public void testSwitchInputMethod_noNewFillRequest() throws Exception {
+        // Set service
+        enableService();
+
+        // Set expectations
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "sweet")
+                        .setPresentation(createPresentation("The Dude"))
+                        .build());
+        sReplier.addResponse(builder.build());
+
+        // Trigger auto-fill
+        mActivity.onUsername(View::requestFocus);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertDatasetsContains("The Dude");
+
+        // Trigger IME switch event
+        Helper.mockSwitchInputMethod(sContext);
+        mUiBot.waitForIdleSync();
+
+        // Tap password field
+        mUiBot.selectByRelativeId(ID_PASSWORD);
+        mUiBot.waitForIdleSync();
+
+        mUiBot.assertDatasetsContains("The Dude");
+
+        // No new fill request
+        sReplier.assertNoUnhandledFillRequests();
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/NonAutofillableActivity.java b/tests/autofillservice/src/android/autofillservice/cts/NonAutofillableActivity.java
new file mode 100644
index 0000000..3233cd4
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/NonAutofillableActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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 android.os.Bundle;
+
+public final class NonAutofillableActivity extends AbstractAutoFillActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.non_autofillable_activity);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
index 541f5e0..47664de 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
@@ -28,6 +28,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.Helper;
 import android.autofillservice.cts.augmented.AugmentedAutofillAutoActivityLaunchTestCase;
 import android.autofillservice.cts.augmented.AugmentedLoginActivity;
 import android.autofillservice.cts.augmented.CannedAugmentedFillResponse;
@@ -281,4 +282,100 @@
         // Expect the inline suggestion to disappear.
         mUiBot.assertNoDatasets();
     }
+
+    @Test
+    public void testSwitchInputMethod() throws Exception {
+        // Set services
+        enableService();
+        enableAugmentedService();
+
+        // Set expectations
+        final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+        sReplier.addResponse(NO_RESPONSE);
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude", createInlinePresentation("dude"))
+                        .build())
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+                        .build(), usernameId)
+                .build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+        sReplier.getNextFillRequest();
+        sAugmentedReplier.getNextFillRequest();
+
+        // Confirm suggestion
+        mUiBot.assertDatasets("dude");
+
+        // Trigger IME switch event
+        Helper.mockSwitchInputMethod(sContext);
+        mUiBot.waitForIdleSync();
+
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me 2")
+                        .setField(usernameId, "dude2", createInlinePresentation("dude2"))
+                        .build())
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req2")
+                        .build(), usernameId)
+                .build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+
+        // Confirm new fill request
+        sAugmentedReplier.getNextFillRequest();
+
+        // Confirm new suggestion
+        mUiBot.assertDatasets("dude2");
+    }
+
+    @Test
+    public void testSwitchInputMethod_mainServiceDisabled() throws Exception {
+        // Set services
+        Helper.disableAutofillService(sContext);
+        enableAugmentedService();
+
+        // Set expectations
+        final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+                        .setField(usernameId, "dude", createInlinePresentation("dude"))
+                        .build())
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+                        .build(), usernameId)
+                .build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+        sAugmentedReplier.getNextFillRequest();
+
+        // Confirm suggestion
+        mUiBot.assertDatasets("dude");
+
+        // Trigger IME switch event
+        Helper.mockSwitchInputMethod(sContext);
+        mUiBot.waitForIdleSync();
+
+        sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+                .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me 2")
+                        .setField(usernameId, "dude2", createInlinePresentation("dude2"))
+                        .build())
+                .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req2")
+                        .build(), usernameId)
+                .build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdle();
+
+        // Confirm new fill request
+        sAugmentedReplier.getNextFillRequest();
+
+        // Confirm new suggestion
+        mUiBot.assertDatasets("dude2");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index 204bb43..e2c6964 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -27,15 +27,23 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.app.PendingIntent;
 import android.autofillservice.cts.CannedFillResponse;
 import android.autofillservice.cts.DummyActivity;
 import android.autofillservice.cts.Helper;
 import android.autofillservice.cts.InstrumentedAutoFillService;
 import android.autofillservice.cts.LoginActivityCommonTestCase;
+import android.autofillservice.cts.NonAutofillableActivity;
+import android.autofillservice.cts.UsernameOnlyActivity;
 import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
 import android.service.autofill.FillContext;
 
+import com.android.cts.mockime.MockImeSession;
+
 import org.junit.Test;
 
 public class InlineLoginActivityTest extends LoginActivityCommonTestCase {
@@ -120,6 +128,57 @@
     }
 
     @Test
+    public void testAutofill_SwitchToAutofillableActivity() throws Exception {
+        assertAutofill_SwitchActivity(UsernameOnlyActivity.class);
+    }
+
+    @Test
+    public void testAutofill_SwitchToNonAutofillableActivity() throws Exception {
+        assertAutofill_SwitchActivity(NonAutofillableActivity.class);
+    }
+
+    private void assertAutofill_SwitchActivity(Class<?> clazz) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setField(ID_PASSWORD, "password")
+                        .setPresentation(createPresentation("The Username"))
+                        .setInlinePresentation(createInlinePresentation("The Username"))
+                        .build());
+        sReplier.addResponse(builder.build());
+
+        // Trigger auto-fill.
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdleSync();
+        sReplier.getNextFillRequest();
+        // Make sure the suggestion is shown.
+        mUiBot.assertDatasets("The Username");
+
+        mUiBot.pressHome();
+        mUiBot.waitForIdle();
+
+        // Switch to another Activity
+        startActivity(clazz);
+        mUiBot.waitForIdle();
+
+        // Trigger input method show.
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdleSync();
+        // Make sure suggestion is not shown.
+        mUiBot.assertNoDatasets();
+    }
+
+    protected final void startActivity(Class<?> clazz) {
+        final Intent intent = new Intent(mContext, clazz);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
+    @Test
     public void testAutofill_selectDatasetThenHideInlineSuggestion() throws Exception {
         // Set service.
         enableService();
@@ -198,4 +257,95 @@
         sReplier.getNextFillRequest();
         mUiBot.waitForIdleSync();
     }
+
+    @Test
+    public void testAutofill_noInvalid() throws Exception {
+        final String keyInvalid = "invalid";
+        final String keyValid = "valid";
+        final String message = "Passes valid message to the remote service";
+        final Bundle bundle = new Bundle();
+        bundle.putBinder(keyInvalid, new Binder());
+        bundle.putString(keyValid, message);
+
+        // Set service.
+        enableService();
+        final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+        assumeTrue("MockIME not available", mockImeSession != null);
+
+        mockImeSession.callSetInlineSuggestionsExtras(bundle);
+
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setPresentation(createPresentation("The Username"))
+                        .setInlinePresentation(createInlinePresentation("The Username"))
+                        .build());
+
+        sReplier.addResponse(builder.build());
+
+        // Trigger auto-fill.
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdleSync();
+
+        mUiBot.assertDatasets("The Username");
+
+        final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
+        final Bundle extras = request.inlineRequest.getExtras();
+        assertThat(extras.get(keyInvalid)).isNull();
+        assertThat(extras.getString(keyValid)).isEqualTo(message);
+
+        final Bundle style = request.inlineRequest.getInlinePresentationSpecs().get(0).getStyle();
+        assertThat(style.get(keyInvalid)).isNull();
+        assertThat(style.getString(keyValid)).isEqualTo(message);
+
+        final Bundle style2 = request.inlineRequest.getInlinePresentationSpecs().get(1).getStyle();
+        assertThat(style2.get(keyInvalid)).isNull();
+        assertThat(style2.getString(keyValid)).isEqualTo(message);
+    }
+
+    @Test
+    public void testSwitchInputMethod() throws Exception {
+        // Set service
+        enableService();
+
+        final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+                .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude")
+                        .setPresentation(createPresentation("The Username"))
+                        .setInlinePresentation(createInlinePresentation("The Username"))
+                        .build());
+
+        sReplier.addResponse(builder.build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdleSync();
+
+        mUiBot.assertDatasets("The Username");
+
+        sReplier.getNextFillRequest();
+
+        // Trigger IME switch event
+        Helper.mockSwitchInputMethod(sContext);
+        mUiBot.waitForIdleSync();
+
+        final CannedFillResponse.Builder builder2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                        .setField(ID_USERNAME, "dude2")
+                        .setPresentation(createPresentation("The Username 2"))
+                        .setInlinePresentation(createInlinePresentation("The Username 2"))
+                        .build());
+
+        sReplier.addResponse(builder2.build());
+
+        // Trigger auto-fill
+        mUiBot.selectByRelativeId(ID_USERNAME);
+        mUiBot.waitForIdleSync();
+
+        // Confirm new suggestion
+        mUiBot.assertDatasets("The Username 2");
+
+        // Confirm new fill request
+        sReplier.getNextFillRequest();
+    }
 }
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 31218db..f853468 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -314,7 +314,7 @@
     ~CaptureResultListener() {
         std::unique_lock<std::mutex> l(mMutex);
         clearSavedRequestsLocked();
-        clearFailedFrameNumbersLocked();
+        clearFailedLostFrameNumbersLocked();
     }
 
     static void onCaptureStart(void* /*obj*/, ACameraCaptureSession* /*session*/,
@@ -496,7 +496,7 @@
         }
         CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
         std::lock_guard<std::mutex> lock(thiz->mMutex);
-        thiz->mLastLostFrameNumber = frameNumber;
+        thiz->mBufferLostFrameNumbers.insert(frameNumber);
         thiz->mResultCondition.notify_one();
     }
 
@@ -524,7 +524,7 @@
         std::unique_lock<std::mutex> l(mMutex);
 
         while ((mLastCompletedFrameNumber != frameNumber) &&
-                (mLastLostFrameNumber != frameNumber) && !checkForFailureLocked(frameNumber)) {
+                !checkForFailureOrLossLocked(frameNumber)) {
             auto timeout = std::chrono::system_clock::now() +
                            std::chrono::seconds(timeoutSec);
             if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
@@ -533,7 +533,7 @@
         }
 
         if ((mLastCompletedFrameNumber == frameNumber) ||
-                (mLastLostFrameNumber == frameNumber) || checkForFailureLocked(frameNumber)) {
+                checkForFailureOrLossLocked(frameNumber)) {
             ret = true;
         }
 
@@ -562,9 +562,9 @@
         }
     }
 
-    bool checkForFailure(int64_t frameNumber) {
+    bool checkForFailureOrLoss(int64_t frameNumber) {
         std::lock_guard<std::mutex> lock(mMutex);
-        return checkForFailureLocked(frameNumber);
+        return checkForFailureOrLossLocked(frameNumber);
     }
 
     void reset() {
@@ -572,10 +572,9 @@
         mLastSequenceIdCompleted = -1;
         mLastSequenceFrameNumber = -1;
         mLastCompletedFrameNumber = -1;
-        mLastLostFrameNumber = -1;
         mSaveCompletedRequests = false;
         clearSavedRequestsLocked();
-        clearFailedFrameNumbersLocked();
+        clearFailedLostFrameNumbersLocked();
     }
 
   private:
@@ -584,8 +583,7 @@
     int mLastSequenceIdCompleted = -1;
     int64_t mLastSequenceFrameNumber = -1;
     int64_t mLastCompletedFrameNumber = -1;
-    int64_t mLastLostFrameNumber = -1;
-    std::set<int64_t> mFailedFrameNumbers;
+    std::set<int64_t> mFailedFrameNumbers, mBufferLostFrameNumbers;
     bool    mSaveCompletedRequests = false;
     std::vector<ACaptureRequest*> mCompletedRequests;
     // Registered physical camera Ids that are being requested upon.
@@ -598,17 +596,23 @@
         mCompletedRequests.clear();
     }
 
-    void clearFailedFrameNumbersLocked() {
+    void clearFailedLostFrameNumbersLocked() {
         mFailedFrameNumbers.clear();
+        mBufferLostFrameNumbers.clear();
     }
 
-    bool checkForFailureLocked(int64_t frameNumber) {
-        return mFailedFrameNumbers.find(frameNumber) != mFailedFrameNumbers.end();
+    bool checkForFailureOrLossLocked(int64_t frameNumber) {
+        return (mFailedFrameNumbers.find(frameNumber) != mFailedFrameNumbers.end()) ||
+                (mBufferLostFrameNumbers.find(frameNumber) != mBufferLostFrameNumbers.end());
     }
 };
 
 class ImageReaderListener {
   public:
+    ImageReaderListener() {
+        mBufferTs.insert(mLastBufferTs);
+    }
+
     // count, acquire, validate, and delete AImage when a new image is available
     static void validateImageCb(void* obj, AImageReader* reader) {
         ALOGV("%s", __FUNCTION__);
@@ -721,11 +725,73 @@
         mDumpFilePathBase = path;
     }
 
+    // acquire image, query the corresponding timestamp but not delete the image
+    static void signalImageCb(void* obj, AImageReader* reader) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        ImageReaderListener* thiz = reinterpret_cast<ImageReaderListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+
+        AImage* img = nullptr;
+        media_status_t ret = AImageReader_acquireNextImage(reader, &img);
+        if (ret != AMEDIA_OK || img == nullptr) {
+            ALOGE("%s: acquire image from reader %p failed! ret: %d, img %p",
+                    __FUNCTION__, reader, ret, img);
+            thiz->mBufferCondition.notify_one();
+            return;
+        }
+
+        int64_t currentTs = -1;
+        ret = AImage_getTimestamp(img, &currentTs);
+        if (ret != AMEDIA_OK || currentTs == -1) {
+            ALOGE("%s: acquire image from reader %p failed! ret: %d", __FUNCTION__, reader, ret);
+            AImage_delete(img);
+            thiz->mBufferCondition.notify_one();
+            return;
+        }
+
+        thiz->mBufferTs.insert(currentTs);
+        thiz->mBufferCondition.notify_one();
+        return;
+    }
+
+    bool waitForNextBuffer(uint32_t timeoutSec) {
+        bool ret = false;
+        std::unique_lock<std::mutex> l(mMutex);
+
+        auto it = mBufferTs.find(mLastBufferTs);
+        if (it == mBufferTs.end()) {
+            ALOGE("%s: Last buffer timestamp: %" PRId64 " not found!", __FUNCTION__, mLastBufferTs);
+            return false;
+        }
+        it++;
+
+        if (it == mBufferTs.end()) {
+            auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(timeoutSec);
+            if (std::cv_status::no_timeout == mBufferCondition.wait_until(l, timeout)) {
+                it = mBufferTs.find(mLastBufferTs);
+                it++;
+            }
+        }
+
+        if (it != mBufferTs.end()) {
+            mLastBufferTs = *it;
+            ret = true;
+        }
+
+        return ret;
+    }
+
   private:
     // TODO: add mReader to make sure each listener is associated to one reader?
     std::mutex mMutex;
     int mOnImageAvailableCount = 0;
     const char* mDumpFilePathBase = nullptr;
+    std::condition_variable mBufferCondition;
+    int64_t mLastBufferTs = -1;
+    std::set<int64_t> mBufferTs;
 };
 
 class StaticInfo {
@@ -3573,9 +3639,10 @@
     return pass;
 }
 
-// Test the camera NDK capture failure path by acquiring the maximum amount of ImageReader
-// buffers available. Since there is no circulation of camera images, the registered output
-// surface will eventually run out of free buffers and start reporting capture errors.
+// Test the camera NDK capture failure path by acquiring the maximum amount of
+// ImageReader buffers available. Since there is no circulation of camera
+// images, the registered output surface will eventually run out of free buffers
+// and start reporting capture errors or lost buffers.
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
 testCameraDeviceCaptureFailureNative(JNIEnv* env, jclass /*clazz*/, jstring jOverrideCameraId) {
@@ -3588,6 +3655,7 @@
     int numCameras = 0;
     bool pass = false;
     PreviewTestCase testCase;
+    uint32_t bufferTimeoutSec = 1;
     uint32_t timeoutSec = 10; // It is important to keep this timeout bigger than the framework
                               // timeout
 
@@ -3636,7 +3704,7 @@
 
         ImageReaderListener readerListener;
         AImageReader_ImageListener readerCb =
-                { &readerListener, ImageReaderListener::acquireImageCb };
+                { &readerListener, ImageReaderListener::signalImageCb };
         mediaRet = testCase.initImageReaderWithErrorLog(TEST_WIDTH, TEST_HEIGHT,
                 AIMAGE_FORMAT_YUV_420_888, NUM_TEST_IMAGES, &readerCb);
         if (mediaRet != AMEDIA_OK) {
@@ -3692,7 +3760,8 @@
                         cameraId);
                 goto exit;
             }
-            auto failedFrameNumber = resultListener.checkForFailure(lastFrameNumber) ?
+            readerListener.waitForNextBuffer(bufferTimeoutSec);
+            auto failedFrameNumber = resultListener.checkForFailureOrLoss(lastFrameNumber) ?
                     lastFrameNumber : -1;
             if (lastFailedRequestNumber != failedFrameNumber) {
                 if ((lastFailedRequestNumber + 1) == failedFrameNumber) {
diff --git a/tests/framework/base/windowmanager/jetpack/Android.bp b/tests/framework/base/windowmanager/jetpack/Android.bp
index 860f63c..f330299 100644
--- a/tests/framework/base/windowmanager/jetpack/Android.bp
+++ b/tests/framework/base/windowmanager/jetpack/Android.bp
@@ -35,6 +35,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "compatibility-device-util-axt",
+        "cts-wm-util",
         "platform-test-annotations",
     ],
     libs: [
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
index 7b9bcf6..8880904 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
@@ -58,7 +58,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ExtensionTest extends JetpackExtensionTestBase {
     private ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(
-            TestActivity.class, false /* initialTouchMode */, true /* launchActivity */);
+            TestActivity.class, false /* initialTouchMode */, false /* launchActivity */);
     private ActivityTestRule<TestConfigChangeHandlingActivity> mConfigHandlingActivityTestRule =
             new ActivityTestRule<>(TestConfigChangeHandlingActivity.class,
                     false /* initialTouchMode */, false /* launchActivity */);
@@ -80,8 +80,12 @@
     private IBinder mWindowToken;
 
     @Before
-    public void setUp() {
-        mActivity = mActivityTestRule.getActivity();
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Launch activity after the ActivityManagerTestBase clean all package states.
+        mActivity = mActivityTestRule.launchActivity(new Intent());
         ExtensionUtils.assumeSupportedDevice(mActivity);
 
         mExtension = ExtensionUtils.getInterfaceCompat(mActivity);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/JetpackExtensionTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/JetpackExtensionTestBase.java
index d460241..905e33e 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/JetpackExtensionTestBase.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/JetpackExtensionTestBase.java
@@ -18,9 +18,10 @@
 
 import android.app.Activity;
 import android.os.IBinder;
+import android.server.wm.ActivityManagerTestBase;
 
 /** Base class for all tests in the module. Copied from androidx.window.WindowTestBase. */
-class JetpackExtensionTestBase {
+class JetpackExtensionTestBase extends ActivityManagerTestBase {
     static IBinder getActivityWindowToken(Activity activity) {
         return activity.getWindow().getAttributes().token;
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
index e3a5328..756426a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.withSettings;
 
 import android.platform.test.annotations.Presubmit;
+import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimation.Bounds;
@@ -225,5 +226,27 @@
                 insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
     }
 
+    @Test
+    public void testAnimationCallbacks_withLegacyFlags() {
+        getInstrumentation().runOnMainSync(() -> {
+            mActivity.getWindow().setDecorFitsSystemWindows(true);
+            mRootView.setSystemUiVisibility(
+                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+            mRootView.post(() -> {
+                mRootView.getWindowInsetsController().hide(systemBars());
+            });
+        });
+
+        getWmState().waitFor(state -> !state.isWindowVisible("StatusBar"),
+                "Waiting for status bar to be hidden");
+        assertFalse(getWmState().isWindowVisible("StatusBar"));
+
+        verify(mActivity.mCallback).onPrepare(any());
+        verify(mActivity.mCallback).onStart(any(), any());
+        verify(mActivity.mCallback, atLeastOnce()).onProgress(any(), any());
+        verify(mActivity.mCallback).onEnd(any());
+    }
 
 }
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 1f1d4e8..77391b9 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
@@ -192,11 +192,12 @@
     private static final List<String> TEST_PACKAGES;
 
     static {
-        final List<String> testPackages = new ArrayList<>(3);
+        final List<String> testPackages = new ArrayList<>();
         testPackages.add(TEST_PACKAGE);
         testPackages.add(SECOND_TEST_PACKAGE);
         testPackages.add(THIRD_TEST_PACKAGE);
         testPackages.add("android.server.wm.cts");
+        testPackages.add("android.server.wm.jetpack");
         TEST_PACKAGES = Collections.unmodifiableList(testPackages);
     }
 
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index f6bf355..5ee276a 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -296,6 +296,9 @@
                         final int height = command.getExtras().getInt("height");
                         mView.setHeight(height);
                         return ImeEvent.RETURN_VALUE_UNAVAILABLE;
+                    case "setInlineSuggestionsExtras":
+                        mInlineSuggestionsExtras = command.getExtras();
+                        return ImeEvent.RETURN_VALUE_UNAVAILABLE;
                 }
             }
             return ImeEvent.RETURN_VALUE_UNAVAILABLE;
@@ -303,6 +306,9 @@
     }
 
     @Nullable
+    private Bundle mInlineSuggestionsExtras;
+
+    @Nullable
     private CommandReceiver mCommandReceiver;
 
     @Nullable
@@ -613,7 +619,13 @@
     @Override
     public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
         getTracer().onStartInputView(editorInfo, restarting,
-                () -> super.onStartInputView(editorInfo, restarting));
+                () -> {
+                    super.onStartInputView(editorInfo, restarting);
+                    final PendingInlineSuggestions pendingInlineSuggestions =
+                            new PendingInlineSuggestions();
+                    pendingInlineSuggestions.mValid.set(true);
+                    mView.updateInlineSuggestions(pendingInlineSuggestions);
+                });
     }
 
     @Override
@@ -716,6 +728,13 @@
         final AtomicInteger mInflatedViewCount;
         final AtomicBoolean mValid = new AtomicBoolean(true);
 
+        PendingInlineSuggestions() {
+            mResponse = null;
+            mTotalCount = 0;
+            mViews = null;
+            mInflatedViewCount = null;
+        }
+
         PendingInlineSuggestions(InlineSuggestionsResponse response) {
             mResponse = response;
             mTotalCount = response.getInlineSuggestions().size();
@@ -730,6 +749,10 @@
         StylesBuilder stylesBuilder = UiVersions.newStylesBuilder();
         stylesBuilder.addStyle(InlineSuggestionUi.newStyleBuilder().build());
         Bundle styles = stylesBuilder.build();
+        if (mInlineSuggestionsExtras != null) {
+            styles.putAll(mInlineSuggestionsExtras);
+        }
+
         return getTracer().onCreateInlineSuggestionsRequest(() -> {
             final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
             presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
@@ -737,13 +760,16 @@
             presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
                     new Size(400, 100)).setStyle(styles).build());
 
-            return new InlineSuggestionsRequest.Builder(presentationSpecs)
-                    .setMaxSuggestionCount(6)
-                    .build();
+            final InlineSuggestionsRequest.Builder builder =
+                    new InlineSuggestionsRequest.Builder(presentationSpecs)
+                            .setMaxSuggestionCount(6);
+            if (mInlineSuggestionsExtras != null) {
+                builder.setExtras(mInlineSuggestionsExtras.deepCopy());
+            }
+            return builder.build();
         });
     }
 
-
     @MainThread
     @Override
     public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 9090648..699da4c 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -1001,4 +1001,9 @@
         params.putInt("height", height);
         return callCommandInternal("setHeight", params);
     }
+
+    @NonNull
+    public ImeCommand callSetInlineSuggestionsExtras(@NonNull Bundle bundle) {
+        return callCommandInternal("setInlineSuggestionsExtras", bundle);
+    }
 }
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
new file mode 100644
index 0000000..9122c58
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 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.view.inputmethod.cts;
+
+import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
+import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeInvisible;
+import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeVisible;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEventWithKeyValue;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.util.Pair;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.view.inputmethod.cts.util.TestUtils;
+import android.view.inputmethod.cts.util.UnlockScreenRule;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ImeInsetsVisibilityTest extends EndToEndImeTestBase {
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final int NEW_KEYBOARD_HEIGHT = 400;
+
+    private static final String TEST_MARKER_PREFIX =
+            "android.view.inputmethod.cts.ImeInsetsVisibilityTest";
+
+    private static String getTestMarker() {
+        return TEST_MARKER_PREFIX + "/"  + SystemClock.elapsedRealtimeNanos();
+    }
+
+    @Rule
+    public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+
+    @Test
+    public void testImeVisibilityWhenImeFocusableChildPopup() throws Exception {
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final String marker = getTestMarker();
+            final Pair<EditText, TestActivity> editTextTestActivityPair =
+                    launchTestActivity(false, marker);
+            final EditText editText = editTextTestActivityPair.first;
+            final TestActivity activity = editTextTestActivityPair.second;
+
+            notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectImeInvisible(TIMEOUT);
+
+            // Emulate tap event
+            CtsTouchUtils.emulateTapOnViewCenter(
+                    InstrumentationRegistry.getInstrumentation(), null, editText);
+            TestUtils.waitOnMainUntil(() -> editText.hasFocus(), TIMEOUT);
+            WindowInsetsController controller = editText.getWindowInsetsController();
+
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
+                    View.VISIBLE, TIMEOUT);
+            PollingCheck.check("Ime insets should be visible", TIMEOUT,
+                    () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()));
+            expectImeVisible(TIMEOUT);
+
+            final View[] childViewRoot = new View[1];
+            TestUtils.runOnMainSync(() -> {
+                childViewRoot[0] = addChildWindow(activity);
+                childViewRoot[0].setVisibility(View.VISIBLE);
+            });
+            TestUtils.waitOnMainUntil(() -> childViewRoot[0] != null
+                    && childViewRoot[0].getVisibility() == View.VISIBLE, TIMEOUT);
+
+            PollingCheck.check("Ime insets should be visible", TIMEOUT,
+                    () -> editText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()));
+            expectImeVisible(TIMEOUT);
+        }
+    }
+
+    @Test
+    public void testEditTextPositionAndPersistWhenAboveImeWindowShown() throws Exception {
+        final InputMethodManager imm = InstrumentationRegistry.getInstrumentation().getContext()
+                .getSystemService(InputMethodManager.class);
+
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder().setInputViewHeight(NEW_KEYBOARD_HEIGHT))) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            final String marker = getTestMarker();
+            final Pair<EditText, TestActivity> editTextTestActivityPair =
+                    launchTestActivity(true, marker);
+            final EditText editText = editTextTestActivityPair.first;
+            final TestActivity activity = editTextTestActivityPair.second;
+            final WindowInsets[] insetsFromActivity = new WindowInsets[1];
+            Point curEditPos = getLocationOnScreenForView(editText);
+
+            TestUtils.runOnMainSync(() -> {
+                activity.getWindow().getDecorView().setOnApplyWindowInsetsListener(
+                        (v, insets) -> insetsFromActivity[0] = insets);
+            });
+
+            notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectImeInvisible(TIMEOUT);
+
+            // Emulate tap event to show soft-keyboard
+            CtsTouchUtils.emulateTapOnViewCenter(
+                    InstrumentationRegistry.getInstrumentation(), null, editText);
+            TestUtils.waitOnMainUntil(() -> editText.hasFocus(), TIMEOUT);
+
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
+                    View.VISIBLE, TIMEOUT);
+            expectImeVisible(TIMEOUT);
+
+            Point lastEditTextPos = new Point(curEditPos);
+            curEditPos = getLocationOnScreenForView(editText);
+            assertTrue("Insets should visible and EditText position should be adjusted",
+                    isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime())
+                            && curEditPos.y < lastEditTextPos.y);
+
+            imm.showInputMethodPicker();
+            TestUtils.waitOnMainUntil(() -> imm.isInputMethodPickerShown() && editText.isLaidOut(),
+                    TIMEOUT, "InputMethod picker should be shown");
+            lastEditTextPos = new Point(curEditPos);
+            curEditPos = getLocationOnScreenForView(editText);
+
+            assertTrue("Insets visibility & EditText position should persist when " +
+                            "the above IME window shown",
+                    isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime())
+                            && curEditPos.equals(lastEditTextPos));
+
+            InstrumentationRegistry.getInstrumentation().getContext().sendBroadcast(
+                    new Intent(ACTION_CLOSE_SYSTEM_DIALOGS).setFlags(FLAG_RECEIVER_FOREGROUND));
+            TestUtils.waitOnMainUntil(() -> !imm.isInputMethodPickerShown(), TIMEOUT,
+                    "InputMethod picker should be closed");
+        }
+    }
+
+    private boolean isInsetsVisible(WindowInsets winInsets, int type) {
+        if (winInsets == null) {
+            return false;
+        }
+        return winInsets.isVisible(type);
+    }
+
+    private Point getLocationOnScreenForView(View view) {
+        return TestUtils.getOnMainSync(() -> {
+            final int[] tmpPos = new int[2];
+            view.getLocationOnScreen(tmpPos);
+            return new Point(tmpPos[0], tmpPos[1]);
+        });
+    }
+
+    private Pair<EditText, TestActivity> launchTestActivity(boolean useDialogTheme,
+            @NonNull String focusedMarker) {
+        final AtomicReference<EditText> focusedEditTextRef = new AtomicReference<>();
+        final AtomicReference<TestActivity> testActivityRef = new AtomicReference<>();
+
+        TestActivity.startSync(activity -> {
+            final LinearLayout layout = new LinearLayout(activity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+            layout.setGravity(Gravity.BOTTOM);
+            if (useDialogTheme) {
+                // Create a floating Dialog
+                activity.setTheme(android.R.style.Theme_Material_Dialog);
+                TextView textView = new TextView(activity);
+                textView.setText("I'm a TextView");
+                textView.setHeight(activity.getWindowManager().getMaximumWindowMetrics()
+                        .getBounds().height() / 2);
+                layout.addView(textView);
+            }
+
+            final EditText focusedEditText = new EditText(activity);
+            focusedEditText.setHint("focused editText");
+            focusedEditText.setPrivateImeOptions(focusedMarker);
+
+            focusedEditTextRef.set(focusedEditText);
+            testActivityRef.set(activity);
+
+            layout.addView(focusedEditText);
+            return layout;
+        });
+        return new Pair<>(focusedEditTextRef.get(), testActivityRef.get());
+    }
+
+    private View addChildWindow(Activity activity) {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final WindowManager wm = context.getSystemService(WindowManager.class);
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+        attrs.token = activity.getWindow().getAttributes().token;
+        attrs.type = TYPE_APPLICATION;
+        attrs.width = 200;
+        attrs.height = 200;
+        attrs.format = PixelFormat.TRANSPARENT;
+        attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
+        attrs.setFitInsetsTypes(WindowInsets.Type.ime() | WindowInsets.Type.statusBars()
+                | WindowInsets.Type.navigationBars());
+        final View childViewRoot = new View(context);
+        childViewRoot.setVisibility(View.GONE);
+        wm.addView(childViewRoot, attrs);
+        return childViewRoot;
+    }
+}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
index b7853e8..b6d51f3 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/ScanningSettingsTest.java
@@ -34,6 +34,7 @@
 import android.test.AndroidTestCase;
 
 import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.FeatureUtil;
 import com.android.compatibility.common.util.PollingCheck;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -63,14 +64,17 @@
 
     @Override
     protected void setUp() {
+        // Can't use assumeTrue / assumeFalse because this is not a junit test, and so doesn't
+        // support using these keywords to trigger assumption failure and skip test.
+        if (FeatureUtil.isTV() || FeatureUtil.isAutomotive()) {
+            // TV and auto do not support the setting options of WIFI scanning and Bluetooth
+            // scanning
+            return;
+        }
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
 
         mPackageManager = mContext.getPackageManager();
-        if (isTv()) {
-            // TV does not support the setting options of WIFI scanning and Bluetooth scanning
-            return;
-        }
         final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
         launcherIntent.addCategory(Intent.CATEGORY_HOME);
         mLauncherPackage = mPackageManager.resolveActivity(launcherIntent,
@@ -79,7 +83,7 @@
 
     @CddTest(requirement = "7.4.2/C-2-1")
     public void testWifiScanningSettings() throws Exception {
-        if (isTv()) {
+        if (FeatureUtil.isTV() || FeatureUtil.isAutomotive()) {
             return;
         }
         launchScanningSettings();
@@ -111,7 +115,7 @@
 
     @CddTest(requirement = "7.4.3/C-4-1")
     public void testBleScanningSettings() throws PackageManager.NameNotFoundException {
-        if (isTv()) {
+        if (FeatureUtil.isTV() || FeatureUtil.isAutomotive()) {
             return;
         }
         launchScanningSettings();
@@ -119,11 +123,6 @@
                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE);
     }
 
-    private boolean isTv() {
-        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
-                && mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
     private void launchScanningSettings() {
         // Start from the home screen
         mDevice.pressHome();
diff --git a/tests/location/location_privileged/Android.bp b/tests/location/location_privileged/Android.bp
new file mode 100644
index 0000000..19ab211b
--- /dev/null
+++ b/tests/location/location_privileged/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 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: "CtsLocationPrivilegedTestCases",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.ext.truth",
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "truth-prebuilt",
+    ],
+    libs: [
+        "android.test.base.stubs",
+    ],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+}
diff --git a/tests/location/location_privileged/AndroidManifest.xml b/tests/location/location_privileged/AndroidManifest.xml
new file mode 100644
index 0000000..29df273
--- /dev/null
+++ b/tests/location/location_privileged/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.location.cts.privileged">
+
+    <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:label="CTS tests for android.location"
+                     android:targetPackage="android.location.cts.privileged" >
+        <meta-data android:name="listener"
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/location/location_privileged/AndroidTest.xml b/tests/location/location_privileged/AndroidTest.xml
new file mode 100644
index 0000000..9086e27
--- /dev/null
+++ b/tests/location/location_privileged/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 Location test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="location" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsLocationPrivilegedTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.location.cts.privileged" />
+    </test>
+
+</configuration>
diff --git a/tests/location/location_privileged/README.md b/tests/location/location_privileged/README.md
new file mode 100644
index 0000000..98c24c5
--- /dev/null
+++ b/tests/location/location_privileged/README.md
@@ -0,0 +1 @@
+Location CTS tests that require privileged permissions such as LOCATION_HARDWARE.
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/PrivilegedLocationPermissionTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/PrivilegedLocationPermissionTest.java
new file mode 100644
index 0000000..97c3909
--- /dev/null
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/PrivilegedLocationPermissionTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.privileged;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.Manifest;
+import android.content.Context;
+import android.location.LocationManager;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class PrivilegedLocationPermissionTest {
+
+    private Context mContext;
+    private LocationManager mLocationManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = ApplicationProvider.getApplicationContext();
+        mLocationManager = mContext.getSystemService(LocationManager.class);
+        assertNotNull(mLocationManager);
+
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(Manifest.permission.LOCATION_HARDWARE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testExtraLocationControllerPackage() {
+        // Extra location controller API is only available after Q.
+        if (VERSION.SDK_INT < VERSION_CODES.Q) {
+            return;
+        }
+        String originalPackageName = mLocationManager.getExtraLocationControllerPackage();
+        boolean originalPackageEnabeld = mLocationManager.isExtraLocationControllerPackageEnabled();
+
+        // Test setting extra location controller package.
+        String packageName = mContext.getPackageName();
+        mLocationManager.setExtraLocationControllerPackage(packageName);
+        assertWithMessage("Extra location controller package").that(
+                mLocationManager.getExtraLocationControllerPackage()).isEqualTo(packageName);
+
+        // Test enabling extra location controller package.
+        mLocationManager.setExtraLocationControllerPackageEnabled(!originalPackageEnabeld);
+        assertWithMessage("Extra location controller package enabled").that(
+                mLocationManager.isExtraLocationControllerPackageEnabled()).isEqualTo(
+                !originalPackageEnabeld);
+
+        // Reset the original extra location controller package.
+        mLocationManager.setExtraLocationControllerPackage(originalPackageName);
+        mLocationManager.setExtraLocationControllerPackageEnabled(originalPackageEnabeld);
+    }
+}
diff --git a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
index 35d48df..6942094 100644
--- a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
@@ -24,14 +24,14 @@
 import android.hardware.SensorManager;
 import android.os.Build;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.PropertyUtil;
 
 /**
- * Checks if Hifi sensors  or VR High performance mode sensors
- * are supported. When supported, checks individual support for
- * Accelerometer, Gyroscope, Gyroscope_uncal, GeoMagneticField,
- * MagneticField_uncal Pressure, RotationVector,
- * SignificantMotion, StepDetector, StepCounter, TiltDetector.
+ * Checks if required sensor types are present, for example sensors required
+ * when Hifi sensors or VR High performance mode features are enabled. Also
+ * checks that required composite sensor types are present if the underlying
+ * physical sensors are present.
  *
  * <p>To execute these test cases, the following command can be used:</p>
  * <pre>
@@ -44,6 +44,9 @@
     private boolean mAreHifiSensorsSupported;
     private boolean mVrHighPerformanceModeSupported;
     private boolean mIsVrHeadset;
+    private boolean mHasAccel;
+    private boolean mHasGyro;
+    private boolean mHasMag;
 
     @Override
     public void setUp() {
@@ -57,58 +60,89 @@
             mSensorManager =
                     (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
         }
+
+        mHasAccel = hasSensorType(Sensor.TYPE_ACCELEROMETER);
+        mHasGyro = hasSensorType(Sensor.TYPE_GYROSCOPE);
+        mHasMag = hasSensorType(Sensor.TYPE_MAGNETIC_FIELD);
     }
 
+    @CddTest(requirement="7.3.9/C-2-1")
     public void testSupportsAccelerometer() {
-        checkSupportsSensor(Sensor.TYPE_ACCELEROMETER);
+        checkHifiVrSensorSupport(Sensor.TYPE_ACCELEROMETER);
     }
 
+    @CddTest(requirement="7.3.9/C-2-2")
     public void testSupportsAccelerometerUncalibrated() {
         // Uncalibrated accelerometer was not required before Android O
         if (PropertyUtil.getFirstApiLevel() >= Build.VERSION_CODES.O) {
-            checkSupportsSensor(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED);
+            checkHifiVrSensorSupport(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED);
         }
     }
 
+    @CddTest(requirement="7.3.9/C-2-3")
     public void testSupportsGyroscope() {
-        checkSupportsSensor(Sensor.TYPE_GYROSCOPE);
+        checkHifiVrSensorSupport(Sensor.TYPE_GYROSCOPE);
     }
 
+    @CddTest(requirement="7.3.9/C-2-4")
     public void testSupportsGyroscopeUncalibrated() {
-        checkSupportsSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);
+        checkHifiVrSensorSupport(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);
     }
 
+    @CddTest(requirement="7.3.9/C-2-5")
     public void testSupportsGeoMagneticField() {
-        checkSupportsSensor(Sensor.TYPE_MAGNETIC_FIELD);
+        checkHifiVrSensorSupport(Sensor.TYPE_MAGNETIC_FIELD);
     }
 
+    @CddTest(requirement="7.3.9/C-2-6")
     public void testSupportsMagneticFieldUncalibrated() {
-        checkSupportsSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);
+        checkHifiVrSensorSupport(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);
     }
 
+    @CddTest(requirement="7.3.9/C-2-7")
     public void testSupportsPressure() {
-        checkSupportsSensor(Sensor.TYPE_PRESSURE);
+        checkHifiVrSensorSupport(Sensor.TYPE_PRESSURE);
     }
 
-    public void testSupportsRotationVector() {
-        checkSupportsSensor(Sensor.TYPE_ROTATION_VECTOR);
+    @CddTest(requirement="7.3.9/C-2-8")
+    public void testSupportsGameRotationVector() {
+        checkHifiVrSensorSupport(Sensor.TYPE_GAME_ROTATION_VECTOR);
     }
 
+    @CddTest(requirement="7.3.9/C-2-9")
     public void testSupportsSignificantMotion() {
-        checkSupportsSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+        checkHifiVrSensorSupport(Sensor.TYPE_SIGNIFICANT_MOTION);
     }
 
+    @CddTest(requirement="7.3.9/C-2-10")
     public void testSupportsStepDetector() {
-        checkSupportsSensor(Sensor.TYPE_STEP_DETECTOR);
+        checkHifiVrSensorSupport(Sensor.TYPE_STEP_DETECTOR);
     }
 
+    @CddTest(requirement="7.3.9/C-2-11")
     public void testSupportsStepCounter() {
-        checkSupportsSensor(Sensor.TYPE_STEP_COUNTER);
+        checkHifiVrSensorSupport(Sensor.TYPE_STEP_COUNTER);
     }
 
+    @CddTest(requirement="7.3.9/C-2-12")
     public void testSupportsTiltDetector() {
         final int TYPE_TILT_DETECTOR = 22;
-        checkSupportsSensor(TYPE_TILT_DETECTOR);
+        checkHifiVrSensorSupport(TYPE_TILT_DETECTOR);
+    }
+
+    @CddTest(requirement="7.3.1/C-3-1")
+    public void testSupportsGravityAndLinearAccelIfHasAG() {
+        if (mHasAccel && mHasGyro) {
+            assertTrue(hasSensorType(Sensor.TYPE_GRAVITY));
+            assertTrue(hasSensorType(Sensor.TYPE_LINEAR_ACCELERATION));
+        }
+    }
+
+    @CddTest(requirement="7.3.1/C-4-1")
+    public void testSupportsRotationVectorIfHasAGM() {
+        if (mHasAccel && mHasGyro && mHasMag) {
+            assertTrue(hasSensorType(Sensor.TYPE_ROTATION_VECTOR));
+        }
     }
 
     private boolean sensorRequiredForVrHighPerformanceMode(int sensorType) {
@@ -124,7 +158,7 @@
         }
     }
 
-    private void checkSupportsSensor(int sensorType) {
+    private void checkHifiVrSensorSupport(int sensorType) {
         boolean isVrSensor = mVrHighPerformanceModeSupported &&
             sensorRequiredForVrHighPerformanceMode(sensorType);
         if (mAreHifiSensorsSupported || isVrSensor) {
@@ -135,4 +169,8 @@
             }
         }
     }
+
+    private boolean hasSensorType(int sensorType) {
+        return (mSensorManager.getDefaultSensor(sensorType) != null);
+    }
 }
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index 54a5bc0..ba6a589 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -44,6 +44,8 @@
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -51,6 +53,8 @@
 import java.io.Reader;
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @RunWith(AndroidJUnit4.class)
 @AppModeFull
@@ -59,6 +63,7 @@
 
     private static final String TEST_APK_PATH = "/data/local/tmp/cts/content/";
     private static final String TEST_APK = "HelloWorld5.apk";
+    private static final String TEST_APK_SPLIT = "HelloWorld5_hdpi-v4.apk";
 
     private static UiAutomation getUiAutomation() {
         return InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -71,6 +76,33 @@
         }
     }
 
+    private static String executeShellCommand(String command, File[] inputs)
+            throws IOException {
+        return executeShellCommand(command, inputs, Stream.of(inputs).mapToLong(
+                File::length).toArray());
+    }
+
+    private static String executeShellCommand(String command, File[] inputs, long[] expected)
+            throws IOException {
+        assertEquals(inputs.length, expected.length);
+        final ParcelFileDescriptor[] pfds =
+                InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                        .executeShellCommandRw(command);
+        ParcelFileDescriptor stdout = pfds[0];
+        ParcelFileDescriptor stdin = pfds[1];
+        try (FileOutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(
+                stdin)) {
+            for (int i = 0; i < inputs.length; i++) {
+                try (FileInputStream inputStream = new FileInputStream(inputs[i])) {
+                    writeFullStream(inputStream, outputStream, expected[i]);
+                }
+            }
+        }
+        try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
+            return readFullStream(inputStream);
+        }
+    }
+
     private static String readFullStream(InputStream inputStream, long expected)
             throws IOException {
         ByteArrayOutputStream result = new ByteArrayOutputStream();
@@ -88,7 +120,7 @@
         byte[] buffer = new byte[1024];
         long total = 0;
         int length;
-        while ((length = inputStream.read(buffer)) != -1) {
+        while ((length = inputStream.read(buffer)) != -1 && (expected < 0 || total < expected)) {
             outputStream.write(buffer, 0, length);
             total += length;
         }
@@ -131,7 +163,64 @@
         assertTrue(isAppInstalled(TEST_APP_PACKAGE));
     }
 
-    static class TestDataLoaderService extends DataLoaderService {}
+    @Test
+    public void testInstallWithIdSigAndSplit() throws Exception {
+        File apkfile = new File(createApkPath(TEST_APK));
+        File splitfile = new File(createApkPath(TEST_APK_SPLIT));
+        File[] files = new File[]{apkfile, splitfile};
+        String param = Arrays.stream(files).map(
+                file -> file.getName() + ":" + file.length()).collect(Collectors.joining(" "));
+        assertEquals("Success\n", executeShellCommand(
+                String.format("pm install-incremental -t -g -S %s %s",
+                        (apkfile.length() + splitfile.length()), param),
+                files));
+        assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+        assertEquals("base, config.hdpi", getSplits(TEST_APP_PACKAGE));
+    }
+
+    @Test
+    public void testInstallWithIdSigInvalidLength() throws Exception {
+        File file = new File(createApkPath(TEST_APK));
+        assertTrue(
+                executeShellCommand("pm install-incremental -t -g -S " + (file.length() - 1),
+                        new File[]{file}).contains(
+                        "Failure"));
+        assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+    }
+
+    @Test
+    public void testInstallWithIdSigStreamIncompleteData() throws Exception {
+        File file = new File(createApkPath(TEST_APK));
+        long length = file.length();
+        // Streaming happens in blocks of 1024 bytes, new length will not stream the last block.
+        long newLength = length - (length % 1024 == 0 ? 1024 : length % 1024);
+        assertTrue(
+                executeShellCommand("pm install-incremental -t -g -S " + length,
+                        new File[]{file}, new long[]{newLength}).contains(
+                        "Failure"));
+        assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+    }
+
+    @Test
+    public void testInstallWithIdSigStreamIncompleteDataForSplit() throws Exception {
+        File apkfile = new File(createApkPath(TEST_APK));
+        File splitfile = new File(createApkPath(TEST_APK_SPLIT));
+        long splitLength = splitfile.length();
+        // Don't fully stream the split.
+        long newSplitLength = splitLength - (splitLength % 1024 == 0 ? 1024 : splitLength % 1024);
+        File[] files = new File[]{apkfile, splitfile};
+        String param = Arrays.stream(files).map(
+                file -> file.getName() + ":" + file.length()).collect(Collectors.joining(" "));
+        assertTrue(executeShellCommand(
+                String.format("pm install-incremental -t -g -S %s %s",
+                        (apkfile.length() + splitfile.length()), param),
+                files, new long[]{apkfile.length(), newSplitLength}).contains(
+                "Failure"));
+        assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+    }
+
+    static class TestDataLoaderService extends DataLoaderService {
+    }
 
     @Test
     public void testDataLoaderServiceDefaultImplementation() {
diff --git a/tests/tests/instantapp/Android.bp b/tests/tests/instantapp/Android.bp
new file mode 100644
index 0000000..9109578
--- /dev/null
+++ b/tests/tests/instantapp/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2020 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: "CtsInstantAppTests",
+    srcs: [ "src/**/*.kt" ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    platform_apis: true,
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "vts10",
+        "device-tests",
+    ],
+}
diff --git a/tests/tests/instantapp/AndroidManifest.xml b/tests/tests/instantapp/AndroidManifest.xml
new file mode 100644
index 0000000..394663b
--- /dev/null
+++ b/tests/tests/instantapp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.cts.instantapp.resolver"
+    >
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="CtsInstantAppTests"
+        android:targetPackage="android.cts.instantapp.resolver"
+        />
+
+</manifest>
diff --git a/tests/tests/instantapp/AndroidTest.xml b/tests/tests/instantapp/AndroidTest.xml
new file mode 100644
index 0000000..7e03bce
--- /dev/null
+++ b/tests/tests/instantapp/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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="Test module config for CTSInstantAppTests">
+    <option name="test-tag" value="CTSInstantAppTests" />
+    <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" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsInstantAppTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.cts.instantapp.resolver" />
+    </test>
+</configuration>
diff --git a/tests/tests/instantapp/OWNERS b/tests/tests/instantapp/OWNERS
new file mode 100644
index 0000000..dd85fa9
--- /dev/null
+++ b/tests/tests/instantapp/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+chiuwinson@google.com
+patb@google.com
+toddke@google.com
diff --git a/tests/tests/instantapp/src/android/cts/instantapp/resolver/InstantAppRequestInfoTest.kt b/tests/tests/instantapp/src/android/cts/instantapp/resolver/InstantAppRequestInfoTest.kt
new file mode 100644
index 0000000..defe511
--- /dev/null
+++ b/tests/tests/instantapp/src/android/cts/instantapp/resolver/InstantAppRequestInfoTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.instantapp.resolver
+
+import android.content.Intent
+import android.content.pm.InstantAppRequestInfo
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.util.UUID
+
+class InstantAppRequestInfoTest {
+
+    private val intent = Intent(Intent.ACTION_VIEW)
+    private val hostDigestPrefix = intArrayOf(1)
+    private val userHandle = android.os.Process.myUserHandle()
+    private val isRequesterInstantApp = false
+    private val token = UUID.randomUUID().toString()
+
+    private val info = InstantAppRequestInfo(intent, hostDigestPrefix, userHandle,
+            isRequesterInstantApp, token)
+
+    @Test
+    fun values() {
+        assertValues(info)
+    }
+
+    @Test
+    fun parcel() {
+        Parcel.obtain()
+                .run {
+                    info.writeToParcel(this, 0)
+                    setDataPosition(0)
+                    InstantAppRequestInfo.CREATOR.createFromParcel(this)
+                            .also { recycle() }
+                }
+                .run(::assertValues)
+    }
+
+    private fun assertValues(info: InstantAppRequestInfo) {
+        assertThat(info.intent.filterEquals(intent)).isTrue()
+        assertThat(info.hostDigestPrefix).isEqualTo(hostDigestPrefix)
+        assertThat(info.userHandle).isEqualTo(userHandle)
+        assertThat(info.isRequesterInstantApp).isEqualTo(isRequesterInstantApp)
+        assertThat(info.token).isEqualTo(token)
+    }
+}
diff --git a/tests/tests/instantapp/src/android/cts/instantapp/resolver/ResolverServiceMethodFallbackTest.kt b/tests/tests/instantapp/src/android/cts/instantapp/resolver/ResolverServiceMethodFallbackTest.kt
new file mode 100644
index 0000000..5cb3dfb
--- /dev/null
+++ b/tests/tests/instantapp/src/android/cts/instantapp/resolver/ResolverServiceMethodFallbackTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 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.instantapp.resolver
+
+import android.app.InstantAppResolverService
+import android.app.InstantAppResolverService.InstantAppResolutionCallback
+import android.content.Intent
+import android.content.pm.InstantAppRequestInfo
+import android.net.Uri
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.UserHandle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.UUID
+import kotlin.random.Random
+
+private typealias Method = InstantAppResolverService.(InstantAppRequestInfo) -> Unit
+
+@Suppress("max-line-length")
+@RunWith(Parameterized::class)
+class ResolverServiceMethodFallbackTest @Suppress("UNUSED_PARAMETER") constructor(
+    private val version: Int,
+    private val methodList: List<Method>,
+    private val info: InstantAppRequestInfo,
+    // Remaining only used to print human-readable test name
+    name: String,
+    isWebIntent: Boolean
+) {
+
+    companion object {
+        // Since the resolution callback class is final, mock the IRemoteCallback and have it throw
+        // a unique exception to indicate it was called.
+        class TestRemoteCallbackException : Exception()
+
+        private val testIntentWeb = Intent(Intent.ACTION_VIEW,
+                Uri.parse("https://${this::class.java.canonicalName}.com"))
+        private val testIntentNotWeb = Intent(Intent.ACTION_VIEW,
+                Uri.parse("content://${this::class.java.canonicalName}"))
+
+        private val testRemoteCallback = object : IRemoteCallback {
+            override fun sendResult(data: Bundle?) = throw TestRemoteCallbackException()
+            override fun asBinder() = throw UnsupportedOperationException()
+        }
+        private val testResolutionCallback = InstantAppResolutionCallback(0, testRemoteCallback)
+        private val testArray = IntArray(10) { Random.nextInt() }
+        private val testToken = UUID.randomUUID().toString()
+        private val testUser = UserHandle(Integer.MAX_VALUE)
+        private val testInfoWeb = InstantAppRequestInfo(testIntentWeb, testArray, testUser,
+                false, testToken)
+        private val testInfoNotWeb = InstantAppRequestInfo(testIntentNotWeb, testArray, testUser,
+                false, testToken)
+
+        // Each section defines methods versions with later definitions falling back to
+        // earlier definitions. Each block receives an [InstantAppResolverService] and invokes
+        // the appropriate version with the test data defined above.
+        private val infoOne: Method = { onGetInstantAppResolveInfo(testArray, testToken,
+                testResolutionCallback) }
+        private val infoTwo: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testToken,
+                testResolutionCallback) }
+        private val infoThree: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testUser,
+                testToken, testResolutionCallback) }
+        private val infoFour: Method = { onGetInstantAppResolveInfo(it, testResolutionCallback) }
+
+        private val filterOne: Method = { onGetInstantAppIntentFilter(testArray, testToken,
+                testResolutionCallback) }
+        private val filterTwo: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+                testToken, testResolutionCallback) }
+        private val filterThree: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+                testUser, testToken, testResolutionCallback) }
+        private val filterFour: Method = { onGetInstantAppIntentFilter(it, testResolutionCallback) }
+
+        private val infoList = listOf(infoOne, infoTwo, infoThree, infoFour)
+        private val filterList = listOf(filterOne, filterTwo, filterThree, filterFour)
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{3} version {0}, isWeb = {4}")
+        fun parameters(): Array<Array<*>> {
+            // Sanity check that web intent logic hasn't changed
+            assertThat(testInfoWeb.intent.isWebIntent).isTrue()
+            assertThat(testInfoNotWeb.intent.isWebIntent).isFalse()
+
+            // Declare all the possible params
+            val versions = Array(5) { it }
+            val methods = arrayOf("ResolveInfo" to infoList, "IntentFilter" to filterList)
+            val infos = arrayOf(testInfoWeb, testInfoNotWeb)
+
+            // FlatMap params into every possible combination
+            return infos.flatMap { info ->
+                methods.flatMap { (name, methods) ->
+                    versions.map { version ->
+                        arrayOf(version, methods, info, name, info.intent.isWebIntent)
+                    }
+                }
+            }.toTypedArray()
+        }
+    }
+
+    @field:Mock(answer = Answers.CALLS_REAL_METHODS)
+    lateinit var mockService: InstantAppResolverService
+
+    @get:Rule
+    val expectedException = ExpectedException.none()
+
+    @Before
+    fun setUpMocks() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun onGetInstantApp() {
+        if (version == 0) {
+            // No version of the API was implemented, so expect terminal case
+            if (info.intent.isWebIntent) {
+                // If web intent, terminal is total failure
+                expectedException.expect(IllegalStateException::class.java)
+            } else {
+                // Otherwise, terminal is a fail safe by calling [testRemoteCallback]
+                expectedException.expect(TestRemoteCallbackException::class.java)
+            }
+        } else if (version < 2 && !info.intent.isWebIntent) {
+            // Starting from v2, if resolving a non-web intent and a v2+ method isn't implemented,
+            // it fails safely by calling [testRemoteCallback]
+            expectedException.expect(TestRemoteCallbackException::class.java)
+        }
+
+        // Version 1 is the first method (index 0)
+        val methodIndex = version - 1
+
+        // Implement a method if necessary
+        methodList.getOrNull(methodIndex)?.invoke(doNothing().`when`(mockService), info)
+
+        // Call the latest API
+        methodList.last().invoke(mockService, info)
+
+        // Check all methods before implemented method are never called
+        (0 until methodIndex).forEach {
+            methodList[it].invoke(verify(mockService, never()), info)
+        }
+
+        // Check all methods from implemented method are called
+        (methodIndex until methodList.size).forEach {
+            methodList[it].invoke(verify(mockService), info)
+        }
+
+        verifyNoMoreInteractions(mockService)
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 08b6977..11aeb5e 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -635,13 +635,14 @@
                     maxVolume),
                     minVolume < maxVolume);
 
-            mAudioManager.setStreamVolume(stream, 1, 0);
+            final int minNonZeroVolume = Math.max(minVolume, 1);
+            mAudioManager.setStreamVolume(stream, minNonZeroVolume, 0);
             if (mUseFixedVolume) {
                 assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
                 continue;
             }
             assertEquals(String.format("stream=%d", stream),
-                    1, mAudioManager.getStreamVolume(stream));
+                    minNonZeroVolume, mAudioManager.getStreamVolume(stream));
 
             if (stream == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
                 // due to new regulations, music sent over a wired headset may be volume limited
@@ -681,7 +682,7 @@
             mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
 
             // volume raise
-            mAudioManager.setStreamVolume(stream, 1, 0);
+            mAudioManager.setStreamVolume(stream, minNonZeroVolume, 0);
             volume = mAudioManager.getStreamVolume(stream);
             while (volume < maxVolume) {
                 volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
diff --git a/tests/tests/media/src/android/media/cts/HeifWriterTest.java b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
index 5fe7b65..95f1426 100644
--- a/tests/tests/media/src/android/media/cts/HeifWriterTest.java
+++ b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
@@ -35,6 +35,7 @@
 import android.media.MediaFormat;
 import android.media.MediaMetadataRetriever;
 import android.opengl.GLES20;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -53,6 +54,7 @@
 import androidx.heifwriter.HeifWriter;
 import androidx.test.filters.SmallTest;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.MediaUtils;
 
@@ -78,6 +80,7 @@
     private static final boolean DUMP_YUV_INPUT = false;
     private static final int GRID_WIDTH = 512;
     private static final int GRID_HEIGHT = 512;
+    private static final boolean IS_BEFORE_R = ApiLevelUtil.isBefore(Build.VERSION_CODES.R);
 
     private static byte[][] TEST_YUV_COLORS = {
             {(byte) 255, (byte) 0, (byte) 0},
@@ -364,7 +367,7 @@
                 mRotation = 0;
                 mQuality = 100;
                 // use memfd by default
-                if (DUMP_OUTPUT) {
+                if (DUMP_OUTPUT || IS_BEFORE_R) {
                     mOutputPath = new File(Environment.getExternalStorageDirectory(),
                             OUTPUT_FILENAME).getAbsolutePath();
                 } else {
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index c7d8fa3..0501452 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -24,6 +24,8 @@
 import android.media.MediaCasStateException;
 import android.media.MediaCodec;
 import android.media.MediaDescrambler;
+import android.media.cts.R;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.platform.test.annotations.Presubmit;
@@ -32,6 +34,8 @@
 import android.util.Log;
 
 import androidx.test.filters.SmallTest;
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
 import com.android.compatibility.common.util.PropertyUtil;
 
 import java.lang.ArrayIndexOutOfBoundsException;
@@ -52,6 +56,7 @@
     private static final int sInvalidSystemId = 0;
     private static final int sClearKeySystemId = 0xF6D8;
     private static final int API_LEVEL_BEFORE_CAS_SESSION = 28;
+    private boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
 
     // ClearKey CAS/Descrambler test vectors
     private static final String sProvisionStr =
@@ -252,8 +257,12 @@
         MediaDescrambler descrambler = null;
 
         try {
-            mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
-                android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
+            if (mIsAtLeastR) {
+                mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
+                    android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
+            } else {
+                mediaCas = new MediaCas(sClearKeySystemId);
+            }
             descrambler = new MediaDescrambler(sClearKeySystemId);
 
             mediaCas.provision(sProvisionStr);
@@ -266,7 +275,9 @@
                 fail("Can't open session for program");
             }
 
-            Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
+            if (mIsAtLeastR) {
+                Log.d(TAG, "Session Id = " + Arrays.toString(session.getSessionId()));
+            }
 
             session.setPrivateData(pvtData);
 
@@ -295,7 +306,9 @@
             Handler handler = new Handler(thread.getLooper());
             testEventEcho(mediaCas, 1, 2, null /* data */, handler);
             testSessionEventEcho(mediaCas, session, 1, 2, null /* data */, handler);
-            testOpenSessionEcho(mediaCas, 0, 2, handler);
+            if (mIsAtLeastR) {
+                testOpenSessionEcho(mediaCas, 0, 2, handler);
+            }
             thread.interrupt();
 
             String eventDataString = "event data string";
@@ -504,6 +517,7 @@
      */
     public void testResourceLostEvent() throws Exception {
         MediaCas mediaCas = null;
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
 
         try {
             mediaCas = new MediaCas(getContext(), sClearKeySystemId, "TIS_Session_1",
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 845cc9c..42c9f29 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -35,6 +35,7 @@
 import android.media.MediaRecorder;
 import android.media.cts.R;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
@@ -44,7 +45,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.MediaUtils;
+
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
@@ -84,6 +87,7 @@
             Color.valueOf(0.64f, 0.0f, 0.64f),
             Color.valueOf(0.64f, 0.64f, 0.0f),
     };
+    private boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
 
     @Override
     protected void setUp() throws Exception {
@@ -386,6 +390,7 @@
     }
 
     public void testGenreParsing() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         Object [][] genres = {
             { R.raw.id3test0, null },
             { R.raw.id3test1, "Country" },
@@ -675,48 +680,56 @@
     }
 
     public void testGetFrameAtTimePreviousSyncEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2000000, 60 }, { 2433334, 60 }, { 2533334, 60 }, { 2933334, 60 }, { 3133334, 90}};
         testGetFrameAtTimeEditList(OPTION_PREVIOUS_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeNextSyncEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2000000, 60 }, { 2433334, 90 }, { 2533334, 90 }, { 2933334, 90 }, { 3133334, 120}};
         testGetFrameAtTimeEditList(OPTION_NEXT_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeClosestSyncEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2000000, 60 }, { 2433334, 60 }, { 2533334, 90 }, { 2933334, 90 }, { 3133334, 90}};
         testGetFrameAtTimeEditList(OPTION_CLOSEST_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeClosestEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2000000, 60 }, { 2433335, 73 }, { 2533333, 76 }, { 2949334, 88 }, { 3117334, 94}};
         testGetFrameAtTimeEditList(OPTION_CLOSEST, testCases);
     }
 
     public void testGetFrameAtTimePreviousSyncEmptyNormalEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2133000, 60 }, { 2566334, 60 }, { 2666334, 60 }, { 3100000, 60 }, { 3266000, 90}};
         testGetFrameAtTimeEmptyNormalEditList(OPTION_PREVIOUS_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeNextSyncEmptyNormalEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {{ 2000000, 60 }, { 2133000, 60 }, { 2566334, 90 }, { 3100000, 90 },
                 { 3200000, 120}};
         testGetFrameAtTimeEmptyNormalEditList(OPTION_NEXT_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeClosestSyncEmptyNormalEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2133000, 60 }, { 2566334, 60 }, { 2666000, 90 }, { 3133000, 90 }, { 3200000, 90}};
         testGetFrameAtTimeEmptyNormalEditList(OPTION_CLOSEST_SYNC, testCases);
     }
 
     public void testGetFrameAtTimeClosestEmptyNormalEditList() {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         int[][] testCases = {
                 { 2133000, 60 }, { 2566000, 73 }, { 2666000, 76 }, { 3066001, 88 }, { 3255000, 94}};
         testGetFrameAtTimeEmptyNormalEditList(OPTION_CLOSEST, testCases);
@@ -915,6 +928,7 @@
 
     private void testGetScaledFrameAtTime(int scaleToWidth, int scaleToHeight,
             int expectedWidth, int expectedHeight, Bitmap.Config config) {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         MediaMetadataRetriever.BitmapParams params = null;
         Bitmap bitmap = null;
         if (config != null) {
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 44e724f..cef3d82 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -41,6 +41,7 @@
 import android.media.MicrophoneInfo;
 import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
 import android.opengl.GLES20;
+import android.os.Build;
 import android.os.ConditionVariable;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
@@ -54,6 +55,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.MediaUtils;
 
@@ -132,6 +134,8 @@
 
     private final static String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
 
+    private boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+
     public MediaRecorderTest() {
         super("android.media.cts", MediaStubActivity.class);
         OUTPUT_PATH = new File(Environment.getExternalStorageDirectory(),
@@ -1755,6 +1759,7 @@
     }
 
     public void testPrivacySensitive() throws Exception {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         if (!hasMicrophone() || !hasAac()) {
             MediaUtils.skipTest("no audio codecs or microphone");
             return;
@@ -1768,6 +1773,7 @@
     }
 
     public void testPrivacySensitiveDefaults() throws Exception {
+        if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         if (!hasMicrophone() || !hasAac()) {
             MediaUtils.skipTest("no audio codecs or microphone");
             return;
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
index f006990..a545b09 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -97,8 +97,12 @@
         String systemBrand = getProperty("ro.product.system.brand");
         String systemModel = getProperty("ro.product.system.model");
         String systemProduct = getProperty("ro.product.system.name");
-        if (("Android".equals(systemBrand) || "generic".equals(systemBrand)) &&
-            (systemModel.startsWith("AOSP on ") || systemProduct.startsWith("aosp_"))) {
+        // not all devices may have a system_ext partition
+        String systemExtProduct = getProperty("ro.product.system_ext.name");
+        if (("Android".equals(systemBrand) || "generic".equals(systemBrand) ||
+                "mainline".equals(systemBrand)) &&
+            (systemModel.startsWith("AOSP on ") || systemProduct.startsWith("aosp_")
+                || systemExtProduct.startsWith("aosp_"))) {
             return true;
         }
         return false;
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
index 93a6d91..82b7413 100644
--- a/tests/tests/net/Android.bp
+++ b/tests/tests/net/Android.bp
@@ -39,6 +39,7 @@
 
     static_libs: [
         "FrameworksNetCommonTests",
+        "TestNetworkStackLib",
         "core-tests-support",
         "compatibility-device-util-axt",
         "cts-net-utils",
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionDigitalSignatureTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionDigitalSignatureTest.java
new file mode 100644
index 0000000..9be1dc7
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionDigitalSignatureTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2020 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.net.ipsec.ike.cts;
+
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.net.ipsec.ike.testutils.CertUtils;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Explicitly test setting up transport mode Child SA so that devices do not have
+ * FEATURE_IPSEC_TUNNELS will be test covered. Tunnel mode Child SA setup has been tested in
+ * IkeSessionPskTest and authentication method is orthogonal to Child mode.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IkeSessionDigitalSignatureTest extends IkeSessionTestBase {
+    private static final int EXPECTED_AUTH_REQ_FRAG_COUNT = 3;
+
+    private static final String IKE_INIT_RESP =
+            "46B8ECA1E0D72A18BF3FA1C2CB1EE86F21202220000000000000015022000030"
+                    + "0000002C010100040300000C0100000C800E0100030000080300000503000008"
+                    + "0200000400000008040000022800008800020000328451C8A976CE69E407966A"
+                    + "50D7320C4197A15A07267CE1B16BAFF9BDBBDEC1FDCDAAF7175ADF9AA8DB55DB"
+                    + "2D70C012D01D914C4EDEF6E8B226868EA1D01B2ED0C4C5C86E6BFE566010EC0C"
+                    + "33BA1C93666430B88BDA0470D82CC4F4416F49E3E361E3017C9F27811A66718B"
+                    + "389E1800915D776D59AA528A7E1D1B7815D35144290000249FE8FABE7F43D917"
+                    + "CE370DE2FD9C22BBC082951AC26C1BA26DE795470F2C25BC2900001C00004004"
+                    + "AE388EC86D6D1A470D44142D01AB2E85A7AC14182900001C0000400544A235A4"
+                    + "171C884286B170F48FFC181DB428D87D290000080000402E290000100000402F"
+                    + "00020003000400050000000800004014";
+    private static final String IKE_AUTH_RESP_FRAG_1 =
+            "46B8ECA1E0D72A18BF3FA1C2CB1EE86F3520232000000001000004E0240004C4"
+                    + "00010002DF6750A2D1D5675006F9F6230BB886FFD20CFB973FD04963CFD7A528"
+                    + "560598C58CC44178B2FCBBBBB271387AC81A664B7E7F1055B912F8C686E287C9"
+                    + "D31684C66339151AB86DA3CF1DA664052FA97687634558A1E9E6B37E16A86BD1"
+                    + "68D76DA5E2E1E0B7E98EB662D80D542307015D2BF134EBBBE425D6954FE8C2C4"
+                    + "D31D16C16AA0521C3C481F873ECF25BB8B05AC6083775C1821CAAB1E35A3955D"
+                    + "85ACC599574142E1DD5B262D6E5365CBF6EBE92FFCC16BC29EC3239456F3B202"
+                    + "492551C0F6D752ADCCA56D506D50CC8809EF6BC56EAD005586F7168F76445FD3"
+                    + "1366CC62D32C0C19B28210B8F813F97CD6A447C3857EFD6EC483DDA8ACD9870E"
+                    + "5A21B9C66F0FA44496C0C3D05E8859A1A4CFC88155D0C411BABC13033DD41FA4"
+                    + "AF08CE7734A146687F374F95634D1F26843203CA1FFD05CA3EB150CEA02FBF14"
+                    + "712B7A1C9BC7616A086E7FCA059E7D64EFF98DB895B32F8F7002762AF7D12F23"
+                    + "31E9DD25174C4CE273E5392BBB48F50B7A3E0187181216265F6A4FC7B91BE0AB"
+                    + "C601A580149D4B07411AE99DDB1944B977E86ADC9746605C60A92B569EEFAFFC"
+                    + "3A888D187B75D8F13249689FC28EBCD62B5E03AF171F3A561F0DEA3B1A75F531"
+                    + "971157DCE1E7BC6E7789FF3E8156015BC9C521EFE48996B41471D33BF09864E4"
+                    + "2436E8D7EB6218CDE7716DA754A924B123A63E25585BF27F4AC043A0C4AECE38"
+                    + "BB59DD62F5C0EC657206A76CED1BD26262237DA1CA6815435992A825758DEBEC"
+                    + "DDF598A22B8242AC4E34E70704DBA7B7B73DC3E067C1C98764F8791F84C99156"
+                    + "947D1FFC875F36FCE24B89369C1B5BF1D4C999DCA28E72A528D0E0163C66C067"
+                    + "E71B5E0025C13DA93313942F9EDA230B3ADC254821A4CB1A5DC9D0C5F4DC4E8E"
+                    + "CE46B7B8C72D3C5923C9B30DF1EF7B4EDEDA8BD05C86CA0162AE1BF8F277878E"
+                    + "607401BAA8F06E3EA873FA4C137324C4E0699277CDF649FE7F0F01945EE25FA7"
+                    + "0E4A89737E58185B11B4CB52FD5B0497D3E3CD1CEE7B1FBB3E969DB6F4C324A1"
+                    + "32DC6A0EA21F41332435FD99140C286F8ABBBA926953ADBEED17D30AAD953909"
+                    + "1347EF6D87163D6B1FF32D8B11FFB2E69FAEE7FE913D3826FBA7F9D11E0E3C57"
+                    + "27625B37D213710B5DD8965DAEFD3F491E8C029E2BF361039949BADEC31D60AC"
+                    + "355F26EE41339C03CC9D9B01C3C7F288F0E9D6DFEE78231BDA9AC10FED135913"
+                    + "2836B1A17CE060742B7E5B738A7177CCD59F70337BA251409C377A0FA5333204"
+                    + "D8622BA8C06DE0BEF4F32B6D4D77BE9DE977445D8A2A08C5C38341CB7974FBFB"
+                    + "22C8F983A7D6CEF068DDB2281E6673453521C831C1826861005AE5F37649BC64"
+                    + "0A6360B23284861441A440F1C5AADE1AB53CA63DB17F4C314D493C4C44DE5F20"
+                    + "75E084D080F92791F30BDD88373D50AB5A07BC72B0E7FFFA593103964E55603E"
+                    + "F7FEB7CA0762A1A7B86B6CCAD88CD6CBC7C6935D21F5F06B2700588A2530E619"
+                    + "DA1648AC809F3DDF56ACE5951737568FFEC7E2AB1AA0AE01B03A7F5A29CE73C0"
+                    + "5D2801B17CAAD0121082E9952FAB16BA1C386336C62D4CF3A5019CF61609433E"
+                    + "1C083237D47C4CF575097F7BF9000EF6B6C497A44E6480154A35669AD276BF05"
+                    + "6CC730B4E5962B6AF96CC6D236AE85CEFDA6877173F72D2F614F6696D1F9DF07"
+                    + "E107758B0978F69BC9DBE0CCBF252C40A3FDF7CE9104D3344F7B73593CCD73E0";
+    private static final String IKE_AUTH_RESP_FRAG_2 =
+            "46B8ECA1E0D72A18BF3FA1C2CB1EE86F3520232000000001000000F0000000D4"
+                    + "00020002155211EA41B37BC5F20568A6AE57038EEE208F94F9B444004F1EF391"
+                    + "2CABFCF857B9CD95FAAA9489ED10A3F5C93510820E22E23FC55ED8049E067D72"
+                    + "3645C00E1E08611916CE72D7F0A84123B63A8F3B9E78DBBE39967B7BB074AF4D"
+                    + "BF2178D991EDBDD01908A14A266D09236DB963B14AC33D894F0F83A580209EFD"
+                    + "61875BB56273AA336C22D6A4D890B93E0D42435667830CC32E4F608500E18569"
+                    + "3E6C1D88C0B5AE427333C86468E3474DAA4D1506AAB2A4021309A33DD759D0D0"
+                    + "A8C98BF7FBEA8109361A9F194D0FD756";
+    private static final String DELETE_IKE_RESP =
+            "46B8ECA1E0D72A18BF3FA1C2CB1EE86F2E202520000000020000004C00000030"
+                    + "342842D8DA37C8EFB92ED37C4FBB23CBDC90445137D6A0AF489F9F03641DBA9D"
+                    + "02F6F59FD8A7A78C7261CEB8";
+
+    // Using IPv4 for transport mode Child SA. IPv6 is currently infeasible because the IKE server
+    // that generates the test vectors is running in an IPv4 only network.
+    private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
+            new IkeTrafficSelector(
+                    MIN_PORT,
+                    MAX_PORT,
+                    InetAddresses.parseNumericAddress("172.58.35.103"),
+                    InetAddresses.parseNumericAddress("172.58.35.103"));
+
+    // TODO(b/157510502): Add test for IKE Session setup with transport mode Child in IPv6 network
+
+    private static final String LOCAL_ID_ASN1_DN =
+            "CN=client.test.ike.android.net, O=Android, C=US";
+    private static final String REMOTE_ID_ASN1_DN =
+            "CN=server.test.ike.android.net, O=Android, C=US";
+
+    private static X509Certificate sServerCaCert;
+    private static X509Certificate sClientEndCert;
+    private static X509Certificate sClientIntermediateCaCertOne;
+    private static X509Certificate sClientIntermediateCaCertTwo;
+    private static RSAPrivateKey sClientPrivateKey;
+
+    @BeforeClass
+    public static void setUpCertsBeforeClass() throws Exception {
+        sServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
+        sClientEndCert = CertUtils.createCertFromPemFile("client-a-end-cert.pem");
+        sClientIntermediateCaCertOne =
+                CertUtils.createCertFromPemFile("client-a-intermediate-ca-one.pem");
+        sClientIntermediateCaCertTwo =
+                CertUtils.createCertFromPemFile("client-a-intermediate-ca-two.pem");
+        sClientPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("client-a-private-key.key");
+    }
+
+    private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+        IkeSessionParams ikeParams =
+                new IkeSessionParams.Builder(sContext)
+                        .setNetwork(mTunNetwork)
+                        .setServerHostname(remoteAddress.getHostAddress())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+                        .setLocalIdentification(
+                                new IkeDerAsn1DnIdentification(new X500Principal(LOCAL_ID_ASN1_DN)))
+                        .setRemoteIdentification(
+                                new IkeDerAsn1DnIdentification(
+                                        new X500Principal(REMOTE_ID_ASN1_DN)))
+                        .setAuthDigitalSignature(
+                                sServerCaCert,
+                                sClientEndCert,
+                                Arrays.asList(
+                                        sClientIntermediateCaCertOne, sClientIntermediateCaCertTwo),
+                                sClientPrivateKey)
+                        .build();
+
+        return new IkeSession(
+                sContext,
+                ikeParams,
+                buildTransportModeChildParamsWithTs(
+                        TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
+                mUserCbExecutor,
+                mIkeSessionCallback,
+                mFirstChildSessionCallback);
+    }
+
+    @Test
+    public void testIkeSessionSetupAndChildSessionSetupWithTransportMode() throws Exception {
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(
+                IKE_INIT_RESP,
+                EXPECTED_AUTH_REQ_FRAG_COUNT /* expectedReqPktCnt */,
+                true /* expectedAuthUseEncap */,
+                IKE_AUTH_RESP_FRAG_1,
+                IKE_AUTH_RESP_FRAG_2);
+
+        // IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
+        int expectedMsgId = 2;
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
+                Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+        // Close IKE Session
+        ikeSession.close();
+        performCloseIkeBlocking(expectedMsgId++, DELETE_IKE_RESP);
+        verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
+    }
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionMschapV2Test.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionMschapV2Test.java
new file mode 100644
index 0000000..cb77127
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionMschapV2Test.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 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.net.ipsec.ike.cts;
+
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.net.ipsec.ike.testutils.CertUtils;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Explicitly test setting up transport mode Child SA so that devices do not have
+ * FEATURE_IPSEC_TUNNELS will be test covered. Tunnel mode Child SA setup has been tested in
+ * IkeSessionPskTest and authentication method is orthogonal to Child mode.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IkeSessionMschapV2Test extends IkeSessionTestBase {
+    private static final String IKE_INIT_RESP =
+            "46B8ECA1E0D72A1873F643FF94D249A921202220000000000000015022000030"
+                    + "0000002C010100040300000C0100000C800E0080030000080300000203000008"
+                    + "0200000200000008040000022800008800020000CC6E71E67E32CED6BCE33FBD"
+                    + "A74113867E3FA3AE21C7C9AB44A7F8835DF602BFD6F6528B67FEE39821232380"
+                    + "C99E8FFC0A5D767F8F38906DA41946C2299DF18C15FA69BAC08D3EDB32E8C8CA"
+                    + "28431831561C04CB0CDE393F817151CD8DAF7A311838411F1C39BFDB5EBCF6A6"
+                    + "1DF66DEB067362649D64607D599B56C4227819D0290000241197004CF31AD00F"
+                    + "5E0C92E198488D8A2B6F6A25C82762AA49F565BCE9D857D72900001C00004004"
+                    + "A0D98FEABBFB92A6C0976EE83D2AACFCCF969A6B2900001C0000400575EBF73F"
+                    + "8EE5CC73917DE9D3F91FCD4A16A0444D290000080000402E290000100000402F"
+                    + "00020003000400050000000800004014";
+    private static final String IKE_AUTH_RESP_1_FRAG_1 =
+            "46B8ECA1E0D72A1873F643FF94D249A93520232000000001000004E0240004C4"
+                    + "00010002C4159CB756773B3F1911F4595107BC505D7A28C72F05182966076679"
+                    + "CA68ED92E4BC5CD441C9CB315F2F449A8A521CAFED3C5F285E295FC3791D3415"
+                    + "E3BACF66A08410DF4E35F7D88FE40DA28851C91C77A6549E186AC1B7846DF3FA"
+                    + "0A347A5ABBCAEE19E70F0EE5966DC6242A115F29523709302EDAD2E36C8F0395"
+                    + "CF5C42EC2D2898ECDD8A6AEDD686A70B589A981558667647F32F41E0D8913E94"
+                    + "A6693F53E59EA8938037F562CF1DC5E6E2CDC630B5FFB08949E3172249422F7D"
+                    + "EA069F9BAD5F96E48BADC7164A9269669AD0DF295A80C54D1D23CEA3F28AC485"
+                    + "86D2A9850DA23823037AB7D1577B7B2364C92C36B84238357129EB4A64D33310"
+                    + "B95DCD50CD53E78C32EFE7DC1627D9432E9BFDEE130045DE967B19F92A9D1270"
+                    + "F1E2C6BFBAA56802F3E63510578EF1ECB6872852F286EEC790AA1FE0CAF391CB"
+                    + "E276554922713BA4770CFE71E23F043DC620E22CC02A74F60725D18331B7F2C9"
+                    + "276EB6FBB7CBDAA040046D7ECBE1A5D7064E04E542807C5101B941D1C81B9D5E"
+                    + "90347B22BD4E638E2EDC98E369B51AA29BDB2CF8AA610D4B893EB83A4650717C"
+                    + "38B4D145EE939C18DCEDF6C79933CEB3D7C116B1F188DF9DDD560951B54E4A7D"
+                    + "80C999A32AB02BF39D7B498DAD36F1A5CBE2F64557D6401AE9DD6E0CEADA3F90"
+                    + "540FE9114BB6B8719C9064796354F4A180A6600CAD092F8302564E409B71ACB7"
+                    + "590F19B3AC88E7A606C718D0B97F7E4B4830F11D851C59F2255846DA22E2C805"
+                    + "0CA2AF2ACF3B6C769D11B75B5AC9AB82ED3D90014994B1BF6FED58FBEF2D72EF"
+                    + "8BDFE51F9A101393A7CA1ACF78FAEBF3E3CC25E09407D1E14AF351A159A13EE3"
+                    + "9B919BA8B49942792E7527C2FB6D418C4DF427669A4BF5A1AFBBB973BAF17918"
+                    + "9C9D520CAC2283B89A539ECE785EBE48FBB77D880A17D55C84A51F46068A4B87"
+                    + "FF48FEEE50E1E034CC8AFF5DA92105F55EC4823E67BDFE942CA8BE0DAECBBD52"
+                    + "E8AAF306049DC6C4CF87D987B0AC54FCE92E6AE8507965AAAC6AB8BD3405712F"
+                    + "EE170B70BC64BDCBD86D80C7AAAF341131F9A1210D7430B17218413AE1363183"
+                    + "5C98FA2428B1E9E987ADC9070E232310A28F4C3163E18366FFB112BADD7C5E0F"
+                    + "D13093A7C1428F87856BA0A7E46955589ACA267CE7A04320C4BCDBB60C672404"
+                    + "778F8D511AAB09349DAB482445D7F606F28E7FBBB18FC0F4EC0AF04F44C282F9"
+                    + "39C6E3B955C84DADEA350667236583069B74F492D600127636FA31F63E560851"
+                    + "2FC28B8EA5B4D01D110990B6EA46B9C2E7C7C856C240EF7A8147BA2C4344B85A"
+                    + "453C862024B5B6814D13CDEAEF7683D539BB50CAFFC0416F269F2F9EDEC5FA30"
+                    + "022FD7B4B186CD2020E7ED8D81ED90822EDD8B76F840DD68F09694CFF9B4F33E"
+                    + "11DF4E601A4212881A6D4E9259001705C41E9E23D18A7F3D4A3463649A38211A"
+                    + "5A90D0F17739A677C74E23F31C01D60B5A0F1E6A4D44FED9D25BF1E63418E1FC"
+                    + "0B19F6F4B71DE53C62B14B82279538A82DD4BE19AB6E00AFC20F124AAB7DF21A"
+                    + "42259BE4F40EC69B16917256F23E2C37376311D62E0A3A0EF8C2AD0C090221D5"
+                    + "C5ECA08F08178A4D31FFDB150C609827D18AD83C7B0A43AEE0406BD3FB494B53"
+                    + "A279FDD6447E234C926AD8CE47FFF779BB45B1FC8457C6E7D257D1359959D977"
+                    + "CEF6906A3367DC4D454993EFDC6F1EA94E17EB3DCB00A289346B4CFD7F19B16E";
+    private static final String IKE_AUTH_RESP_1_FRAG_2 =
+            "46B8ECA1E0D72A1873F643FF94D249A935202320000000010000008000000064"
+                    + "00020002C61F66025E821A5E69A4DE1F591A2C32C983C3154A5003660137D685"
+                    + "A5262B9FDF5EDC699DE4D8BD38F549E3CBD12024B45B4C86561C36C3EED839DA"
+                    + "9860C6AA0B764C662D08F1B6A98F68CF6E3038F737C0B415AD8A8B7D702BD92A";
+    private static final String IKE_AUTH_RESP_2 =
+            "46B8ECA1E0D72A1873F643FF94D249A92E202320000000020000008C30000070"
+                    + "62B90C2229FD23025BC2FD7FE6341E9EE04B17264CD619BCE18975A5F88BE438"
+                    + "D4AD4A5310057255AF568C293A29B10107E3EE3675C10AA2B26404D90C0528CC"
+                    + "F7605A86C96A1F2635CCC6CFC90EE65E5C2A2262EB33FE520EB708423A83CB63"
+                    + "274ECCBB102AF5DF35742657";
+    private static final String IKE_AUTH_RESP_3 =
+            "46B8ECA1E0D72A1873F643FF94D249A92E202320000000030000004C30000030"
+                    + "AB52C3C80123D3432C05AF457CE93C352395F73E861CD49561BA528CFE68D17D"
+                    + "78BBF6FC41E81C2B9EA051A2";
+    private static final String IKE_AUTH_RESP_4 =
+            "46B8ECA1E0D72A1873F643FF94D249A92E20232000000004000000CC270000B0"
+                    + "8D3342A7AB2666AC754F4B55C5C6B1A61255E62FBCA53D5CDEEDE60DADB7915C"
+                    + "7F962076A58BF7D39A05ED1B60FF349B6DE311AF7CEBC72B4BB9723A728A5D3E"
+                    + "9E508B2D7A11843D279B56ADA07E608D61F5CA7638F10372A440AD1DCE44E190"
+                    + "7B7B7A68B126EBBB86638D667D5B528D233BA8D32D7E0FAC4E1448E87396EEE6"
+                    + "0985B79841E1229D7962AACFD8F872722EC8D5B19D4C82D6C4ADCB276127A1A7"
+                    + "3FC84CDF85B2299BC96B64AC";
+    private static final String DELETE_IKE_RESP =
+            "46B8ECA1E0D72A1873F643FF94D249A92E202520000000050000004C00000030"
+                    + "622CE06C8CB132AA00567E9BC83F58B32BD7DB5130C76E385B306434DA227361"
+                    + "D50CC19D408A8D4F36F9697F";
+
+    // This value is align with the test vectors hex that are generated in an IPv4 environment
+    private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
+            new IkeTrafficSelector(
+                    MIN_PORT,
+                    MAX_PORT,
+                    InetAddresses.parseNumericAddress("172.58.35.67"),
+                    InetAddresses.parseNumericAddress("172.58.35.67"));
+
+    private static final EapSessionConfig EAP_CONFIG =
+            new EapSessionConfig.Builder()
+                    .setEapIdentity(EAP_IDENTITY)
+                    .setEapMsChapV2Config(EAP_MSCHAPV2_USERNAME, EAP_MSCHAPV2_PASSWORD)
+                    .build();
+
+    private static X509Certificate sServerCaCert;
+
+    @BeforeClass
+    public static void setUpCertBeforeClass() throws Exception {
+        sServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
+    }
+
+    private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+        IkeSessionParams ikeParams =
+                new IkeSessionParams.Builder(sContext)
+                        .setNetwork(mTunNetwork)
+                        .setServerHostname(remoteAddress.getHostAddress())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+                        .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+                        .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+                        .setAuthEap(sServerCaCert, EAP_CONFIG)
+                        .build();
+        return new IkeSession(
+                sContext,
+                ikeParams,
+                buildTransportModeChildParamsWithTs(
+                        TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
+                mUserCbExecutor,
+                mIkeSessionCallback,
+                mFirstChildSessionCallback);
+    }
+
+    @Test
+    public void testIkeSessionSetupAndChildSessionSetupWithTransportMode() throws Exception {
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        int expectedMsgId = 0;
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                false /* expectedUseEncap */,
+                IKE_INIT_RESP);
+
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                IKE_AUTH_RESP_1_FRAG_1,
+                IKE_AUTH_RESP_1_FRAG_2);
+
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                IKE_AUTH_RESP_2);
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                IKE_AUTH_RESP_3);
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                expectedMsgId++,
+                true /* expectedUseEncap */,
+                IKE_AUTH_RESP_4);
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
+                Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+        // Close IKE Session
+        ikeSession.close();
+        performCloseIkeBlocking(expectedMsgId++, DELETE_IKE_RESP);
+        verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
+    }
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
index fb93398..0509fc0 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
@@ -16,39 +16,34 @@
 
 package android.net.ipsec.ike.cts;
 
-import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
+import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-
-import static com.android.internal.util.HexDump.hexStringToByteArray;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
-import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.LinkAddress;
 import android.net.ipsec.ike.IkeFqdnIdentification;
 import android.net.ipsec.ike.IkeSession;
-import android.net.ipsec.ike.IkeSessionConfiguration;
-import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.TunnelModeChildSessionParams;
-import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
+@AppModeFull(reason = "MANAGE_IPSEC_TUNNELS permission can't be granted to instant apps")
 public class IkeSessionPskTest extends IkeSessionTestBase {
     // Test vectors for success workflow
     private static final String SUCCESS_IKE_INIT_RESP =
@@ -89,16 +84,6 @@
                     + "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8"
                     + "6743A7CEB2BE34AC00095A5B8";
 
-    private static final long IKE_INIT_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
-
-    private static final TunnelModeChildSessionParams CHILD_PARAMS =
-            new TunnelModeChildSessionParams.Builder()
-                    .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
-                    .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
-                    .addInternalAddressRequest(AF_INET)
-                    .addInternalAddressRequest(AF_INET6)
-                    .build();
-
     private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
         IkeSessionParams ikeParams =
                 new IkeSessionParams.Builder(sContext)
@@ -113,110 +98,159 @@
         return new IkeSession(
                 sContext,
                 ikeParams,
-                CHILD_PARAMS,
+                buildTunnelModeChildSessionParams(),
                 mUserCbExecutor,
                 mIkeSessionCallback,
                 mFirstChildSessionCallback);
     }
 
+    @BeforeClass
+    public static void setUpTunnelPermissionBeforeClass() throws Exception {
+        // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
+        // a standard permission is insufficient. So we shell out the appop, to give us the
+        // right appop permissions.
+        setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
+    }
+
+    // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
+    // methods.
+    @AfterClass
+    public static void tearDownTunnelPermissionAfterClass() throws Exception {
+        setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
+    }
+
     @Test
     public void testIkeSessionSetupAndChildSessionSetupWithTunnelMode() throws Exception {
         if (!hasTunnelsFeature()) return;
 
         // Open IKE Session
         IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
-        int expectedMsgId = 0;
-        mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
-                expectedMsgId++,
-                false /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_IKE_INIT_RESP));
+        performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
 
-        mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
-                expectedMsgId++,
-                true /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_IKE_AUTH_RESP));
+        // IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
+        int expectedMsgId = 2;
 
-        // Verify opening IKE Session
-        IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
-        assertNotNull(ikeConfig);
-        assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
-        assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
-        assertTrue(ikeConfig.getPcscfServers().isEmpty());
-        assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+                Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+                Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR));
 
-        IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
-        assertNotNull(ikeConnectInfo);
-        assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
-        assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
-        assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
-
-        // Verify opening first Child Session
-        ChildSessionConfiguration firstChildConfig = mFirstChildSessionCallback.awaitChildConfig();
-        assertNotNull(firstChildConfig);
-        assertEquals(
-                Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors());
-        assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors());
-        assertEquals(
-                Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR),
-                firstChildConfig.getInternalAddresses());
-        assertTrue(firstChildConfig.getInternalSubnets().isEmpty());
-        assertTrue(firstChildConfig.getInternalDnsServers().isEmpty());
-        assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty());
-
-        assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
-        assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
 
         // Open additional Child Session
         TestChildSessionCallback additionalChildCb = new TestChildSessionCallback();
-        ikeSession.openChildSession(CHILD_PARAMS, additionalChildCb);
+        ikeSession.openChildSession(buildTunnelModeChildSessionParams(), additionalChildCb);
         mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
+                IKE_DETERMINISTIC_INITIATOR_SPI,
                 expectedMsgId++,
                 true /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_CREATE_CHILD_RESP));
+                SUCCESS_CREATE_CHILD_RESP);
 
         // Verify opening additional Child Session
-        ChildSessionConfiguration additionalChildConfig = additionalChildCb.awaitChildConfig();
-        assertNotNull(additionalChildConfig);
-        assertEquals(
-                Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors());
-        assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors());
-        assertTrue(additionalChildConfig.getInternalAddresses().isEmpty());
-        assertTrue(additionalChildConfig.getInternalSubnets().isEmpty());
-        assertTrue(additionalChildConfig.getInternalDnsServers().isEmpty());
-        assertTrue(additionalChildConfig.getInternalDhcpServers().isEmpty());
-
-        assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
-        assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
+        verifyChildSessionSetupBlocking(
+                additionalChildCb,
+                Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+                Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord additionalTransformRecordA =
+                additionalChildCb.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord additionalTransformRecordB =
+                additionalChildCb.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(additionalTransformRecordA, additionalTransformRecordB);
 
         // Close additional Child Session
         ikeSession.closeChildSession(additionalChildCb);
         mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
+                IKE_DETERMINISTIC_INITIATOR_SPI,
                 expectedMsgId++,
                 true /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_DELETE_CHILD_RESP));
+                SUCCESS_DELETE_CHILD_RESP);
 
-        assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
-        assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
+        verifyDeleteIpSecTransformPair(
+                additionalChildCb, additionalTransformRecordA, additionalTransformRecordB);
         additionalChildCb.awaitOnClosed();
 
         // Close IKE Session
         ikeSession.close();
-        mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
-                expectedMsgId++,
-                true /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_DELETE_IKE_RESP));
+        performCloseIkeBlocking(expectedMsgId++, SUCCESS_DELETE_IKE_RESP);
+        verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
+    }
 
-        assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
-        assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
-        mFirstChildSessionCallback.awaitOnClosed();
-        mIkeSessionCallback.awaitOnClosed();
+    @Test
+    public void testIkeSessionSetupAndChildSessionSetupWithTunnelModeV6() throws Exception {
+        if (!hasTunnelsFeature()) return;
 
-        // TODO: verify created and deleted IpSecTransform pair and their directions
+        final String ikeInitResp =
+                "46B8ECA1E0D72A186F7B6C2CEB77EB9021202220000000000000011822000030"
+                        + "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+                        + "0200000500000008040000022800008800020000DABAA04B38B491E2403F2125"
+                        + "96ECF1C8EF7B1DC19A422FDD46E1756C826BB3A16404361B775D9950577B5CDF"
+                        + "6AAA1642BD1427BDA8BC55354A97C1025E19C1E2EE2DF8A0C9406E545D829F52"
+                        + "75695008E3B742984B8DD1770F3514213B0DF3EE8B199416DF200D248115C057"
+                        + "1C193E4F96802E5EF48DD99CAC251882A8F7CCC329000024BC6F0F1D3653C2C7"
+                        + "679E02CDB6A3B32B2FEE9AF52F0326D4D9AE073D56CE8922290000080000402E"
+                        + "290000100000402F00020003000400050000000800004014";
+        final String ikeAuthResp =
+                "46B8ECA1E0D72A186F7B6C2CEB77EB902E202320000000010000015024000134"
+                        + "4D115AFDCDAD0310760BB664EB7D405A340869AD6EDF0AAEAD0663A9253DADCB"
+                        + "73EBE5CD29D4FA1CDEADE0B94391B5C4CF77BCC1596ACE3CE6A7891E44888FA5"
+                        + "46632C0EF4E6193C023C9DC59142C37D1C49D6EF5CD324EC6FC35C89E1721C78"
+                        + "91FDCDB723D8062709950F4AA9273D26A54C9C7E86862DBC15F7B6641D2B9BAD"
+                        + "E55069008201D12968D97B537B1518FE87B0FFA03C3EE6012C06721B1E2A3F68"
+                        + "92108BC4A4F7063F7F94562D8B60F291A1377A836CF12BCDA7E15C1A8F3C77BB"
+                        + "6DB7F2C833CCE4CDDED7506536621A3356CE2BC1874E7B1A1A9B447D7DF6AB09"
+                        + "638B8AD94A781B28BB91B514B611B24DF8E8A047A10AE27BBF15C754D3D2F792"
+                        + "D3E1CCADDAE934C98AE53A8FC3419C88AFF0355564F82A629C998012DA7BB704"
+                        + "5307270DF326377E3E1994476902035B";
+        final String deleteIkeResp =
+                "46B8ECA1E0D72A186F7B6C2CEB77EB902E202520000000020000005000000034"
+                        + "CF15C299F35688E5140A48B61C95F004121BF8236201415E5CD45BA41AAB16D4"
+                        + "90B44B9E6D5D92B5B97D24196A58C73F";
+
+        mLocalAddress = IPV6_ADDRESS_LOCAL;
+        mRemoteAddress = IPV6_ADDRESS_REMOTE;
+
+        // Teardown current test network that uses IPv4 address and set up new network with IPv6
+        // address.
+        tearDownTestNetwork();
+        setUpTestNetwork(mLocalAddress);
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(
+                ikeInitResp,
+                1 /* expectedAuthReqPktCnt */,
+                false /* expectedAuthUseEncap */,
+                ikeAuthResp);
+
+        // Local request message ID starts from 2 because there is one IKE_INIT message and a single
+        // IKE_AUTH message.
+        int expectedMsgId = 2;
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TUNNEL_MODE_INBOUND_TS_V6),
+                Arrays.asList(TUNNEL_MODE_OUTBOUND_TS_V6),
+                Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR_V6),
+                Arrays.asList(EXPECTED_DNS_SERVERS_ONE, EXPECTED_DNS_SERVERS_TWO));
+
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+        // Close IKE Session
+        ikeSession.close();
+        performCloseIkeBlocking(expectedMsgId++, false /* expectedUseEncap */, deleteIkeResp);
+        verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
     }
 
     @Test
@@ -225,18 +259,7 @@
 
         // Open IKE Session
         IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
-        int expectedMsgId = 0;
-        mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
-                expectedMsgId++,
-                false /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_IKE_INIT_RESP));
-
-        mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
-                expectedMsgId++,
-                true /* expectedUseEncap */,
-                hexStringToByteArray(SUCCESS_IKE_AUTH_RESP));
+        performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
 
         ikeSession.kill();
         mFirstChildSessionCallback.awaitOnClosed();
@@ -245,29 +268,94 @@
 
     @Test
     public void testIkeInitFail() throws Exception {
-        String ikeInitFailRespHex =
+        final String ikeInitFailRespHex =
                 "46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
 
         // Open IKE Session
         IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
         int expectedMsgId = 0;
         mTunUtils.awaitReqAndInjectResp(
-                IKE_INIT_SPI,
+                IKE_DETERMINISTIC_INITIATOR_SPI,
                 expectedMsgId++,
                 false /* expectedUseEncap */,
-                hexStringToByteArray(ikeInitFailRespHex));
+                ikeInitFailRespHex);
 
         mFirstChildSessionCallback.awaitOnClosed();
 
-        IkeException exception = mIkeSessionCallback.awaitOnClosedException();
-        assertNotNull(exception);
-        assertTrue(exception instanceof IkeProtocolException);
-        IkeProtocolException protocolException = (IkeProtocolException) exception;
+        IkeProtocolException protocolException =
+                (IkeProtocolException) mIkeSessionCallback.awaitOnClosedException();
         assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, protocolException.getErrorType());
         assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
     }
 
-    // TODO(b/155821007): Verify rekey process and handling IKE_AUTH failure
+    @Test
+    public void testIkeAuthHandlesAuthFailNotification() throws Exception {
+        final String ikeInitRespHex =
+                "46B8ECA1E0D72A18CF94CE3159486F002120222000000000000001502200"
+                        + "00300000002C010100040300000C0100000C800E01000300000803000005"
+                        + "0300000802000004000000080400000228000088000200001821AA854691"
+                        + "FA3292DF710F0AC149ACBD0CB421608B8796C1912AF04C5B4B23936FDEC4"
+                        + "7CB640E3EAFB56BBB562825E87AF68B40E4BAB80A49BAD44407450A4195A"
+                        + "1DD54BD99F48D28C9F0FBA315A3401C1C3C4AD55911F514A8DF2D2467C46"
+                        + "A73DDC1452AE81336E0F0D5EC896D2E7A77628AF2F9089F48943399DF216"
+                        + "EFCD2900002418D2B7E4E6AF0FEFF5962CF8D68F7793B1293FEDE13331D4"
+                        + "AB0CE9436C2EE1EC2900001C0000400457BD9AEF5B362A83DD7F3DDAA4A9"
+                        + "9B6B4041DAF32900001C000040055A81893582701E44D4B6729A22FE06DE"
+                        + "82A03A36290000080000402E290000100000402F00020003000400050000"
+                        + "000800004014";
+        final String ikeAuthFailRespHex =
+                "46B8ECA1E0D72A18CF94CE3159486F002E202320000000010000004C2900"
+                        + "00301B9E4C8242D3BE62E7F0A537FE8B92C6EAB7153105DA421DCE43A06D"
+                        + "AB6E4808BAC0CA1DAD6ADD0A126A41BD";
 
-    // TODO(b/155821007): Test creating transport mode Child SA
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthFailRespHex);
+
+        mFirstChildSessionCallback.awaitOnClosed();
+        IkeProtocolException protocolException =
+                (IkeProtocolException) mIkeSessionCallback.awaitOnClosedException();
+        assertEquals(ERROR_TYPE_AUTHENTICATION_FAILED, protocolException.getErrorType());
+        assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
+    }
+
+    @Test
+    public void testIkeAuthHandlesFirstChildCreationFail() throws Exception {
+        final String ikeInitRespHex =
+                "46B8ECA1E0D72A182B300285DA19E6452120222000000000000001502200"
+                        + "00300000002C010100040300000C0100000C800E01000300000803000005"
+                        + "0300000802000004000000080400000228000088000200005C9DE629981F"
+                        + "DB1FC45DB6CCF15D076C1F51BD9F63C771DC089F05CCDE6247965D15C616"
+                        + "C7B5A62342491715E4D1FEA19326477D24143E8E56AB6AD93F54B19BC32A"
+                        + "44BC0A5B5632E57D0A3C43E466E1547D8E4EF65EA4B864A348161666E229"
+                        + "84975A486251A17C4F096A6D5CF3DB83874B70324A31AA7ADDE2D73BADD8"
+                        + "238029000024CF06260F7C4923295E7C91F2B8479212892DA7A519A0322F"
+                        + "F5B2BF570B92972B2900001C00004004C7ACC2C7D58CF8C9F5E953993AF4"
+                        + "6CAC976635B42900001C00004005B64B190DFE7BDE8B9B1475EDE67B63D6"
+                        + "F1DBBF44290000080000402E290000100000402F00020003000400050000"
+                        + "000800004014";
+        final String ikeAuthCreateChildFailHex =
+                "46B8ECA1E0D72A182B300285DA19E6452E202320000000010000008C2400"
+                        + "0070386FC9CCC67495A17915D0544390A2963A769F4A42C6FA668CEEC07F"
+                        + "EC0C87D681DE34267023DD394F1401B5A563E71002C0CE0928D0ABC0C4570"
+                        + "E39C2EDEF820F870AB71BD70A3F3EB5C96CA294B6D3F01677690DCF9F8CFC"
+                        + "9584650957573502BA83E32F18207A9ADEB1FA";
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthCreateChildFailHex);
+
+        // Even though the child creation failed, the authentication succeeded, so the IKE Session's
+        // onOpened() callback is still expected
+        verifyIkeSessionSetupBlocking();
+
+        // Verify Child Creation failed
+        IkeProtocolException protocolException =
+                (IkeProtocolException) mFirstChildSessionCallback.awaitOnClosedException();
+        assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, protocolException.getErrorType());
+        assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
+
+        ikeSession.kill();
+        mIkeSessionCallback.awaitOnClosed();
+    }
 }
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionRekeyTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionRekeyTest.java
new file mode 100644
index 0000000..f954fcd
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionRekeyTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2020 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.net.ipsec.ike.cts;
+
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.cts.IkeTunUtils.PortPair;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Explicitly test transport mode Child SA so that devices without FEATURE_IPSEC_TUNNELS can be test
+ * covered. Tunnel mode Child SA setup has been tested in IkeSessionPskTest. Rekeying process is
+ * independent from Child SA mode.
+ */
+@RunWith(AndroidJUnit4.class)
+public class IkeSessionRekeyTest extends IkeSessionTestBase {
+    // This value is align with the test vectors hex that are generated in an IPv4 environment
+    private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
+            new IkeTrafficSelector(
+                    MIN_PORT,
+                    MAX_PORT,
+                    InetAddresses.parseNumericAddress("172.58.35.40"),
+                    InetAddresses.parseNumericAddress("172.58.35.40"));
+
+    private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+        IkeSessionParams ikeParams =
+                new IkeSessionParams.Builder(sContext)
+                        .setNetwork(mTunNetwork)
+                        .setServerHostname(remoteAddress.getHostAddress())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+                        .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+                        .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+                        .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+                        .setAuthPsk(IKE_PSK)
+                        .build();
+        return new IkeSession(
+                sContext,
+                ikeParams,
+                buildTransportModeChildParamsWithTs(
+                        TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
+                mUserCbExecutor,
+                mIkeSessionCallback,
+                mFirstChildSessionCallback);
+    }
+
+    private byte[] buildInboundPkt(PortPair outPktSrcDestPortPair, String inboundDataHex)
+            throws Exception {
+        // Build inbound packet by flipping the outbound packet addresses and ports
+        return IkeTunUtils.buildIkePacket(
+                mRemoteAddress,
+                mLocalAddress,
+                outPktSrcDestPortPair.dstPort,
+                outPktSrcDestPortPair.srcPort,
+                true /* useEncap */,
+                hexStringToByteArray(inboundDataHex));
+    }
+
+    @Test
+    public void testRekeyIke() throws Exception {
+        final String ikeInitResp =
+                "46B8ECA1E0D72A1866B5248CF6C7472D21202220000000000000015022000030"
+                        + "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+                        + "0200000500000008040000022800008800020000920D3E830E7276908209212D"
+                        + "E5A7F2A48706CFEF1BE8CB6E3B173B8B4E0D8C2DC626271FF1B13A88619E569E"
+                        + "7B03C3ED2C127390749CDC7CDC711D0A8611E4457FFCBC4F0981B3288FBF58EA"
+                        + "3E8B70E27E76AE70117FBBCB753660ADDA37EB5EB3A81BED6A374CCB7E132C2A"
+                        + "94BFCE402DC76B19C158B533F6B1F2ABF01ACCC329000024B302CA2FB85B6CF4"
+                        + "02313381246E3C53828D787F6DFEA6BD62D6405254AEE6242900001C00004004"
+                        + "7A1682B06B58596533D00324886EF1F20EF276032900001C00004005BF633E31"
+                        + "F9984B29A62E370BB2770FC09BAEA665290000080000402E290000100000402F"
+                        + "00020003000400050000000800004014";
+        final String ikeAuthResp =
+                "46B8ECA1E0D72A1866B5248CF6C7472D2E20232000000001000000F0240000D4"
+                        + "10166CA8647F56123DE74C17FA5E256043ABF73216C812EE32EE1BB01EAF4A82"
+                        + "DC107AB3ADBFEE0DEA5EEE10BDD5D43178F4C975C7C775D252273BB037283C7F"
+                        + "236FE34A6BCE4833816897075DB2055B9FFD66DFA45A0A89A8F70AFB59431EED"
+                        + "A20602FB614369D12906D3355CF7298A5D25364ABBCC75A9D88E0E6581449FCD"
+                        + "4E361A39E00EFD1FD0A69651F63DB46C12470226AA21BA5EFF48FAF0B6DDF61C"
+                        + "B0A69392CE559495EEDB4D1C1D80688434D225D57210A424C213F7C993D8A456"
+                        + "38153FBD194C5E247B592D1D048DB4C8";
+        final String rekeyIkeCreateReq =
+                "46B8ECA1E0D72A1866B5248CF6C7472D2E202400000000000000013021000114"
+                        + "13743670039E308A8409BA5FD47B67F956B36FEE88AC3B70BB5D789B8218A135"
+                        + "1B3D83E260E87B3EDB1BF064F09D4DC2611AEDBC99951B4B2DE767BD4AA2ACC3"
+                        + "3653549CFC66B75869DF003CDC9A137A9CC27776AD5732B34203E74BE8CA4858"
+                        + "1D5C0D9C9CA52D680EB299B4B21C7FA25FFEE174D57015E0FF2EAED653AAD95C"
+                        + "071ABE269A8C2C9FBC1188E07550EB992F910D4CA9689E44BA66DE0FABB2BDF9"
+                        + "8DD377186DBB25EF9B68B027BB2A27981779D8303D88D7CE860010A42862D50B"
+                        + "1E0DBFD3D27C36F14809D7F493B2B96A65534CF98B0C32AD5219AD77F681AC04"
+                        + "9D5CB89A0230A91A243FA7F16251B0D9B4B65E7330BEEAC9663EF4578991EAC8"
+                        + "46C19EBB726E7D113F1D0D601102C05E";
+        final String rekeyIkeDeleteReq =
+                "46B8ECA1E0D72A1866B5248CF6C7472D2E20250000000001000000502A000034"
+                        + "02E40C0C7B1ED977729F705BB9B643FAC513A1070A6EB28ECD2AEA8A441ADC05"
+                        + "7841382A7967BBF116AE52496590B2AD";
+        final String deleteIkeReq =
+                "7D3DEDC65407D1FC9361C8CF8C47162A2E20250800000000000000502A000034"
+                        + "201915C9E4E9173AA9EE79F3E02FE2D4954B22085C66D164762C34D347C16E9F"
+                        + "FC5F7F114428C54D8D915860C57B1BC1";
+        final long newIkeDeterministicInitSpi = Long.parseLong("7D3DEDC65407D1FC", 16);
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        PortPair localRemotePorts = performSetupIkeAndFirstChildBlocking(ikeInitResp, ikeAuthResp);
+
+        // Local request message ID starts from 2 because there is one IKE_INIT message and a single
+        // IKE_AUTH message.
+        int expectedReqMsgId = 2;
+        int expectedRespMsgId = 0;
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
+                Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord firstTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord firstTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+        // Inject rekey IKE requests
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyIkeCreateReq));
+        mTunUtils.awaitResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyIkeDeleteReq));
+        mTunUtils.awaitResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+
+        // IKE has been rekeyed, reset message IDs
+        expectedReqMsgId = 0;
+        expectedRespMsgId = 0;
+
+        // Inject delete IKE request
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, deleteIkeReq));
+        mTunUtils.awaitResp(
+                newIkeDeterministicInitSpi, expectedRespMsgId++, true /* expectedUseEncap */);
+
+        verifyDeleteIpSecTransformPair(
+                mFirstChildSessionCallback, firstTransformRecordA, firstTransformRecordB);
+        mFirstChildSessionCallback.awaitOnClosed();
+        mIkeSessionCallback.awaitOnClosed();
+    }
+
+    @Test
+    public void testRekeyTransportModeChildSa() throws Exception {
+        final String ikeInitResp =
+                "46B8ECA1E0D72A18CECD871146CF83A121202220000000000000015022000030"
+                        + "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+                        + "0200000500000008040000022800008800020000C4904458957746BCF1C12972"
+                        + "1D4E19EB8A584F78DE673053396D167CE0F34552DBC69BA63FE7C673B4CF4A99"
+                        + "62481518EE985357876E8C47BAAA0DBE9C40AE47B12E52165874703586E8F786"
+                        + "045F72EEEB238C5D1823352BED44B71B3214609276ADC0B3D42DAC820168C4E2"
+                        + "660730DAAC92492403288805EBB9053F1AB060DA290000242D9364ACB93519FF"
+                        + "8F8B019BAA43A40D699F59714B327B8382216EF427ED52282900001C00004004"
+                        + "06D91438A0D6B734E152F76F5CC55A72A2E38A0A2900001C000040052EFF78B3"
+                        + "55B37F3CE75AFF26C721B050F892C0D6290000080000402E290000100000402F"
+                        + "00020003000400050000000800004014";
+        final String ikeAuthResp =
+                "46B8ECA1E0D72A18CECD871146CF83A12E20232000000001000000F0240000D4"
+                        + "A17BC258BA2714CF536663639DD5F665A60C75E93557CD5141990A8CEEDD2017"
+                        + "93F5B181C8569FBCD6C2A00198EC2B62D42BEFAC016B8B6BF6A7BC9CEDE3413A"
+                        + "6C495A6B8EC941864DC3E08F57D015EA6520C4B05884960B85478FCA53DA5F17"
+                        + "9628BB1097DA77461C71837207A9EB80720B3E6E661816EE4E14AC995B5E8441"
+                        + "A4C3F9097CC148142BA300076C94A23EC4ADE82B1DD2B121F7E9102860A8C3BF"
+                        + "58DDC207285A3176E924C44DE820322524E1AA438EFDFBA781B36084AED80846"
+                        + "3B77FCED9682B6E4E476408EF3F1037E";
+        final String rekeyChildCreateReq =
+                "46B8ECA1E0D72A18CECD871146CF83A12E202400000000000000015029000134"
+                        + "319D74B6B155B86942143CEC1D29D21F073F24B7BEDC9BFE0F0FDD8BDB5458C0"
+                        + "8DB93506E1A43DD0640FE7370C97F9B34FF4EC9B2DB7257A87B75632301FB68A"
+                        + "86B54871249534CA3D01C9BEB127B669F46470E1C8AAF72574C3CEEC15B901CF"
+                        + "5A0D6ADAE59C3CA64AC8C86689C860FAF9500E608DFE63F2DCD30510FD6FFCD5"
+                        + "A50838574132FD1D069BCACD4C7BAF45C9B1A7689FAD132E3F56DBCFAF905A8C"
+                        + "4145D4BA1B74A54762F8F43308D94DE05649C49D885121CE30681D51AC1E3E68"
+                        + "AB82F9A19B99579AFE257F32DBD1037814DA577379E4F42DEDAC84502E49C933"
+                        + "9EA83F6F5DB4401B660CB1681B023B8603D205DFDD1DE86AD8DE22B6B754F30D"
+                        + "05EAE81A709C2CEE81386133DC3DC7B5EF8F166E48E54A0722DD0C64F4D00638"
+                        + "40F272144C47F6ECED72A248180645DB";
+        final String rekeyChildDeleteReq =
+                "46B8ECA1E0D72A18CECD871146CF83A12E20250000000001000000502A000034"
+                        + "02D98DAF0432EBD991CA4F2D89C1E0EFABC6E91A3327A85D8914FB2F1485BE1B"
+                        + "8D3415D548F7CE0DC4224E7E9D0D3355";
+        final String deleteIkeReq =
+                "46B8ECA1E0D72A18CECD871146CF83A12E20250000000002000000502A000034"
+                        + "095041F4026B4634F04B0AB4F9349484F7BE9AEF03E3733EEE293330043B75D2"
+                        + "ABF5F965ED51127629585E1B1BBA787F";
+
+        // Open IKE Session
+        IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+        PortPair localRemotePorts = performSetupIkeAndFirstChildBlocking(ikeInitResp, ikeAuthResp);
+
+        // IKE INIT and IKE AUTH takes two exchanges. Local request message ID starts from 2
+        int expectedReqMsgId = 2;
+        int expectedRespMsgId = 0;
+
+        verifyIkeSessionSetupBlocking();
+        verifyChildSessionSetupBlocking(
+                mFirstChildSessionCallback,
+                Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
+                Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
+                new ArrayList<LinkAddress>());
+        IpSecTransformCallRecord oldTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord oldTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(oldTransformRecordA, oldTransformRecordB);
+
+        // Inject rekey Child requests
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyChildCreateReq));
+        mTunUtils.awaitResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyChildDeleteReq));
+        mTunUtils.awaitResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+
+        // Verify IpSecTransforms are renewed
+        IpSecTransformCallRecord newTransformRecordA =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        IpSecTransformCallRecord newTransformRecordB =
+                mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+        verifyCreateIpSecTransformPair(newTransformRecordA, newTransformRecordB);
+        verifyDeleteIpSecTransformPair(
+                mFirstChildSessionCallback, oldTransformRecordA, oldTransformRecordB);
+
+        // Inject delete IKE request
+        mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, deleteIkeReq));
+        mTunUtils.awaitResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
+
+        verifyDeleteIpSecTransformPair(
+                mFirstChildSessionCallback, newTransformRecordA, newTransformRecordB);
+        mFirstChildSessionCallback.awaitOnClosed();
+        mIkeSessionCallback.awaitOnClosed();
+    }
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
index 279d088..2458b25 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
@@ -15,7 +15,13 @@
 
 package android.net.ipsec.ike.cts;
 
-import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
@@ -23,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.InetAddresses;
+import android.net.IpSecManager;
 import android.net.IpSecTransform;
 import android.net.LinkAddress;
 import android.net.Network;
@@ -33,7 +40,11 @@
 import android.net.ipsec.ike.ChildSessionConfiguration;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TransportModeChildSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.cts.IkeTunUtils.PortPair;
 import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -55,6 +66,11 @@
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -79,13 +95,39 @@
     // Package-wide common expected results that will be shared by all IKE/Child SA creation tests
     static final String EXPECTED_REMOTE_APP_VERSION_EMPTY = "";
     static final byte[] EXPECTED_PROTOCOL_ERROR_DATA_NONE = new byte[0];
+
+    static final InetAddress EXPECTED_DNS_SERVERS_ONE =
+            InetAddresses.parseNumericAddress("8.8.8.8");
+    static final InetAddress EXPECTED_DNS_SERVERS_TWO =
+            InetAddresses.parseNumericAddress("8.8.4.4");
+
     static final InetAddress EXPECTED_INTERNAL_ADDR =
             InetAddresses.parseNumericAddress("198.51.100.10");
     static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR =
             new LinkAddress(EXPECTED_INTERNAL_ADDR, IP4_PREFIX_LEN);
-    static final IkeTrafficSelector EXPECTED_INBOUND_TS =
+    static final InetAddress EXPECTED_INTERNAL_ADDR_V6 =
+            InetAddresses.parseNumericAddress("2001:db8::2");
+    static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR_V6 =
+            new LinkAddress(EXPECTED_INTERNAL_ADDR_V6, IP6_PREFIX_LEN);
+
+    static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS =
             new IkeTrafficSelector(
                     MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR, EXPECTED_INTERNAL_ADDR);
+    static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS = DEFAULT_V4_TS;
+    static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS_V6 =
+            new IkeTrafficSelector(
+                    MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR_V6, EXPECTED_INTERNAL_ADDR_V6);
+    static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS_V6 = DEFAULT_V6_TS;
+
+    // This value is align with the test vectors hex that are generated in an IPv4 environment
+    static final IkeTrafficSelector TRANSPORT_MODE_OUTBOUND_TS =
+            new IkeTrafficSelector(
+                    MIN_PORT,
+                    MAX_PORT,
+                    InetAddresses.parseNumericAddress("10.138.0.2"),
+                    InetAddresses.parseNumericAddress("10.138.0.2"));
+
+    static final long IKE_DETERMINISTIC_INITIATOR_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
 
     // Static state to reduce setup/teardown
     static Context sContext = InstrumentationRegistry.getContext();
@@ -124,19 +166,12 @@
                 .getUiAutomation()
                 .adoptShellPermissionIdentity();
         sTNM = sContext.getSystemService(TestNetworkManager.class);
-
-        // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
-        // a standard permission is insufficient. So we shell out the appop, to give us the
-        // right appop permissions.
-        setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
     }
 
     // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
     // methods.
     @AfterClass
     public static void tearDownPermissionAfterClass() throws Exception {
-        setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
-
         InstrumentationRegistry.getInstrumentation()
                 .getUiAutomation()
                 .dropShellPermissionIdentity();
@@ -159,7 +194,7 @@
     }
 
     void setUpTestNetwork(InetAddress localAddr) throws Exception {
-        int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP4_PREFIX_LEN;
+        int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP6_PREFIX_LEN;
 
         TestNetworkInterface testIface =
                 sTNM.createTunInterface(new LinkAddress[] {new LinkAddress(localAddr, prefixLen)});
@@ -179,7 +214,7 @@
         mTunFd.close();
     }
 
-    private static void setAppOp(int appop, boolean allow) {
+    static void setAppOp(int appop, boolean allow) {
         String opName = AppOpsManager.opToName(appop);
         for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
             String cmd =
@@ -231,6 +266,78 @@
         }
     }
 
+    TransportModeChildSessionParams buildTransportModeChildParamsWithTs(
+            IkeTrafficSelector inboundTs, IkeTrafficSelector outboundTs) {
+        return new TransportModeChildSessionParams.Builder()
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
+                .addInboundTrafficSelectors(inboundTs)
+                .addOutboundTrafficSelectors(outboundTs)
+                .build();
+    }
+
+    TunnelModeChildSessionParams buildTunnelModeChildSessionParams() {
+        return new TunnelModeChildSessionParams.Builder()
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
+                .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
+                .addInternalAddressRequest(AF_INET)
+                .addInternalAddressRequest(AF_INET6)
+                .build();
+    }
+
+    PortPair performSetupIkeAndFirstChildBlocking(String ikeInitRespHex, String... ikeAuthRespHexes)
+            throws Exception {
+        return performSetupIkeAndFirstChildBlocking(
+                ikeInitRespHex,
+                1 /* expectedAuthReqPktCnt */,
+                true /*expectedAuthUseEncap*/,
+                ikeAuthRespHexes);
+    }
+
+    PortPair performSetupIkeAndFirstChildBlocking(
+            String ikeInitRespHex, boolean expectedAuthUseEncap, String... ikeAuthRespHexes)
+            throws Exception {
+        return performSetupIkeAndFirstChildBlocking(
+                ikeInitRespHex,
+                1 /* expectedAuthReqPktCnt */,
+                expectedAuthUseEncap,
+                ikeAuthRespHexes);
+    }
+
+    PortPair performSetupIkeAndFirstChildBlocking(
+            String ikeInitRespHex,
+            int expectedAuthReqPktCnt,
+            boolean expectedAuthUseEncap,
+            String... ikeAuthRespHexes)
+            throws Exception {
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI,
+                0 /* expectedMsgId */,
+                false /* expectedUseEncap */,
+                ikeInitRespHex);
+
+        byte[] ikeAuthReqPkt =
+                mTunUtils
+                        .awaitReqAndInjectResp(
+                                IKE_DETERMINISTIC_INITIATOR_SPI,
+                                1 /* expectedMsgId */,
+                                expectedAuthUseEncap,
+                                expectedAuthReqPktCnt,
+                                ikeAuthRespHexes)
+                        .get(0);
+        return IkeTunUtils.getSrcDestPortPair(ikeAuthReqPkt);
+    }
+
+    void performCloseIkeBlocking(int expectedMsgId, String deleteIkeRespHex) throws Exception {
+        performCloseIkeBlocking(expectedMsgId, true /* expectedUseEncap*/, deleteIkeRespHex);
+    }
+
+    void performCloseIkeBlocking(
+            int expectedMsgId, boolean expectedUseEncap, String deleteIkeRespHex) throws Exception {
+        mTunUtils.awaitReqAndInjectResp(
+                IKE_DETERMINISTIC_INITIATOR_SPI, expectedMsgId, expectedUseEncap, deleteIkeRespHex);
+    }
+
     /** Testing callback that allows caller to block current thread until a method get called */
     static class TestIkeSessionCallback implements IkeSessionCallback {
         private CompletableFuture<IkeSessionConfiguration> mFutureIkeConfig =
@@ -370,6 +477,109 @@
             this.ipSecTransform = ipSecTransform;
             this.direction = direction;
         }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(ipSecTransform, direction);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof IpSecTransformCallRecord)) return false;
+
+            IpSecTransformCallRecord record = (IpSecTransformCallRecord) o;
+            return ipSecTransform.equals(record.ipSecTransform) && direction == record.direction;
+        }
+    }
+
+    void verifyIkeSessionSetupBlocking() throws Exception {
+        IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
+        assertNotNull(ikeConfig);
+        assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
+        assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
+        assertTrue(ikeConfig.getPcscfServers().isEmpty());
+        assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
+
+        IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
+        assertNotNull(ikeConnectInfo);
+        assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
+        assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
+        assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
+    }
+
+    void verifyChildSessionSetupBlocking(
+            TestChildSessionCallback childCallback,
+            List<IkeTrafficSelector> expectedInboundTs,
+            List<IkeTrafficSelector> expectedOutboundTs,
+            List<LinkAddress> expectedInternalAddresses)
+            throws Exception {
+        verifyChildSessionSetupBlocking(
+                childCallback,
+                expectedInboundTs,
+                expectedOutboundTs,
+                expectedInternalAddresses,
+                new ArrayList<InetAddress>() /* expectedDnsServers */);
+    }
+
+    void verifyChildSessionSetupBlocking(
+            TestChildSessionCallback childCallback,
+            List<IkeTrafficSelector> expectedInboundTs,
+            List<IkeTrafficSelector> expectedOutboundTs,
+            List<LinkAddress> expectedInternalAddresses,
+            List<InetAddress> expectedDnsServers)
+            throws Exception {
+        ChildSessionConfiguration childConfig = childCallback.awaitChildConfig();
+        assertNotNull(childConfig);
+        assertEquals(expectedInboundTs, childConfig.getInboundTrafficSelectors());
+        assertEquals(expectedOutboundTs, childConfig.getOutboundTrafficSelectors());
+        assertEquals(expectedInternalAddresses, childConfig.getInternalAddresses());
+        assertEquals(expectedDnsServers, childConfig.getInternalDnsServers());
+        assertTrue(childConfig.getInternalSubnets().isEmpty());
+        assertTrue(childConfig.getInternalDhcpServers().isEmpty());
+    }
+
+    void verifyCloseIkeAndChildBlocking(
+            IpSecTransformCallRecord expectedTransformRecordA,
+            IpSecTransformCallRecord expectedTransformRecordB)
+            throws Exception {
+        verifyDeleteIpSecTransformPair(
+                mFirstChildSessionCallback, expectedTransformRecordA, expectedTransformRecordB);
+        mFirstChildSessionCallback.awaitOnClosed();
+        mIkeSessionCallback.awaitOnClosed();
+    }
+
+    static void verifyCreateIpSecTransformPair(
+            IpSecTransformCallRecord transformRecordA, IpSecTransformCallRecord transformRecordB) {
+        IpSecTransform transformA = transformRecordA.ipSecTransform;
+        IpSecTransform transformB = transformRecordB.ipSecTransform;
+
+        assertNotNull(transformA);
+        assertNotNull(transformB);
+
+        Set<Integer> expectedDirections = new HashSet<>();
+        expectedDirections.add(IpSecManager.DIRECTION_IN);
+        expectedDirections.add(IpSecManager.DIRECTION_OUT);
+
+        Set<Integer> resultDirections = new HashSet<>();
+        resultDirections.add(transformRecordA.direction);
+        resultDirections.add(transformRecordB.direction);
+
+        assertEquals(expectedDirections, resultDirections);
+    }
+
+    static void verifyDeleteIpSecTransformPair(
+            TestChildSessionCallback childCb,
+            IpSecTransformCallRecord expectedTransformRecordA,
+            IpSecTransformCallRecord expectedTransformRecordB) {
+        Set<IpSecTransformCallRecord> expectedTransforms = new HashSet<>();
+        expectedTransforms.add(expectedTransformRecordA);
+        expectedTransforms.add(expectedTransformRecordB);
+
+        Set<IpSecTransformCallRecord> resultTransforms = new HashSet<>();
+        resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+        resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+
+        assertEquals(expectedTransforms, resultTransforms);
     }
 
     /** Package private method to check if device has IPsec tunnels feature */
@@ -377,7 +587,5 @@
         return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
     }
 
-    // TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth
-
     // TODO(b/148689509): Verify hostname based creation
 }
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
index f07c710..c70e537 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
@@ -54,7 +54,7 @@
     static final int SUB_ID = 1;
     static final byte[] EAP_IDENTITY = "test@android.net".getBytes();
     static final String NETWORK_NAME = "android.net";
-    static final String EAP_MSCHAPV2_USERNAME = "username";
+    static final String EAP_MSCHAPV2_USERNAME = "mschapv2user";
     static final String EAP_MSCHAPV2_PASSWORD = "password";
 
     static final Inet4Address IPV4_ADDRESS_LOCAL =
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
index f52b88b..41cbf0b 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
@@ -26,6 +26,8 @@
 import static android.net.ipsec.ike.cts.PacketUtils.UdpHeader;
 import static android.system.OsConstants.IPPROTO_UDP;
 
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
 import static org.junit.Assert.fail;
 
 import android.os.ParcelFileDescriptor;
@@ -34,7 +36,10 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
 
 public class IkeTunUtils extends TunUtils {
     private static final int PORT_LEN = 2;
@@ -42,63 +47,132 @@
     private static final int NON_ESP_MARKER_LEN = 4;
     private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
 
-    private static final int IKE_HEADER_LEN = 28;
     private static final int IKE_INIT_SPI_OFFSET = 0;
+    private static final int IKE_FIRST_PAYLOAD_OFFSET = 16;
     private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
     private static final int IKE_MSG_ID_OFFSET = 20;
+    private static final int IKE_HEADER_LEN = 28;
+    private static final int IKE_FRAG_NUM_OFFSET = 32;
+    private static final int IKE_PAYLOAD_TYPE_SKF = 53;
+
+    private static final int RSP_FLAG_MASK = 0x20;
 
     public IkeTunUtils(ParcelFileDescriptor tunFd) {
         super(tunFd);
     }
 
     /**
-     * Await the expected IKE request and inject an IKE response.
+     * Await the expected IKE request inject an IKE response (or a list of response fragments)
      *
-     * @param respIkePkt IKE response packet without IP/UDP headers or NON ESP MARKER.
+     * @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
+     *     IP/UDP headers or NON ESP MARKER.
      */
     public byte[] awaitReqAndInjectResp(
-            long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap, byte[] respIkePkt)
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedUseEncap,
+            String... ikeRespDataFragmentsHex)
             throws Exception {
-        byte[] request =
-                awaitIkePacket(
+        return awaitReqAndInjectResp(
                         expectedInitIkeSpi,
                         expectedMsgId,
-                        false /* expectedResp */,
-                        expectedUseEncap);
+                        expectedUseEncap,
+                        1 /* expectedReqPktCnt */,
+                        ikeRespDataFragmentsHex)
+                .get(0);
+    }
+
+    /**
+     * Await the expected IKE request (or the list of IKE request fragments) and inject an IKE
+     * response (or a list of response fragments)
+     *
+     * @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
+     *     IP/UDP headers or NON ESP MARKER.
+     */
+    public List<byte[]> awaitReqAndInjectResp(
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedUseEncap,
+            int expectedReqPktCnt,
+            String... ikeRespDataFragmentsHex)
+            throws Exception {
+        List<byte[]> reqList = new ArrayList<>(expectedReqPktCnt);
+        if (expectedReqPktCnt == 1) {
+            // Expecting one complete IKE packet
+            byte[] req =
+                    awaitIkePacket(
+                            (pkt) -> {
+                                return isExpectedIkePkt(
+                                        pkt,
+                                        expectedInitIkeSpi,
+                                        expectedMsgId,
+                                        false /* expectedResp */,
+                                        expectedUseEncap);
+                            });
+            reqList.add(req);
+        } else {
+            // Expecting "expectedReqPktCnt" number of request fragments
+            for (int i = 0; i < expectedReqPktCnt; i++) {
+                // IKE Fragment number always starts from 1
+                int expectedFragNum = i + 1;
+                byte[] req =
+                        awaitIkePacket(
+                                (pkt) -> {
+                                    return isExpectedIkeFragPkt(
+                                            pkt,
+                                            expectedInitIkeSpi,
+                                            expectedMsgId,
+                                            false /* expectedResp */,
+                                            expectedUseEncap,
+                                            expectedFragNum);
+                                });
+                reqList.add(req);
+            }
+        }
+
+        // All request fragments have the same addresses and ports
+        byte[] request = reqList.get(0);
 
         // Build response header by flipping address and port
         InetAddress srcAddr = getAddress(request, false /* shouldGetSource */);
         InetAddress dstAddr = getAddress(request, true /* shouldGetSource */);
         int srcPort = getPort(request, false /* shouldGetSource */);
         int dstPort = getPort(request, true /* shouldGetSource */);
+        for (String resp : ikeRespDataFragmentsHex) {
+            byte[] response =
+                    buildIkePacket(
+                            srcAddr,
+                            dstAddr,
+                            srcPort,
+                            dstPort,
+                            expectedUseEncap,
+                            hexStringToByteArray(resp));
+            injectPacket(response);
+        }
 
-        byte[] response =
-                buildIkePacket(srcAddr, dstAddr, srcPort, dstPort, expectedUseEncap, respIkePkt);
-        injectPacket(response);
-        return request;
+        return reqList;
     }
 
-    private byte[] awaitIkePacket(
-            long expectedInitIkeSpi,
-            int expectedMsgId,
-            boolean expectedResp,
-            boolean expectedUseEncap)
+    /** Await the expected IKE response */
+    public byte[] awaitResp(long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap)
             throws Exception {
+        return awaitIkePacket(
+                (pkt) -> {
+                    return isExpectedIkePkt(
+                            pkt,
+                            expectedInitIkeSpi,
+                            expectedMsgId,
+                            true /* expectedResp*/,
+                            expectedUseEncap);
+                });
+    }
+
+    private byte[] awaitIkePacket(Predicate<byte[]> pktVerifier) throws Exception {
         long endTime = System.currentTimeMillis() + TIMEOUT;
         int startIndex = 0;
         synchronized (mPackets) {
             while (System.currentTimeMillis() < endTime) {
-                byte[] ikePkt =
-                        getFirstMatchingPacket(
-                                (pkt) -> {
-                                    return isIke(
-                                            pkt,
-                                            expectedInitIkeSpi,
-                                            expectedMsgId,
-                                            expectedResp,
-                                            expectedUseEncap);
-                                },
-                                startIndex);
+                byte[] ikePkt = getFirstMatchingPacket(pktVerifier, startIndex);
                 if (ikePkt != null) {
                     return ikePkt; // We've found the packet we're looking for.
                 }
@@ -112,51 +186,51 @@
                 }
             }
 
-            String direction = expectedResp ? "response" : "request";
-            fail(
-                    "No such IKE "
-                            + direction
-                            + " found with Initiator SPI "
-                            + expectedInitIkeSpi
-                            + " and message ID "
-                            + expectedMsgId);
+            fail("No matching packet found");
         }
 
         throw new IllegalStateException(
                 "Hit an impossible case where fail() didn't throw an exception");
     }
 
-    private static boolean isIke(
+    private static boolean isExpectedIkePkt(
             byte[] pkt,
             long expectedInitIkeSpi,
             int expectedMsgId,
             boolean expectedResp,
             boolean expectedUseEncap) {
-        int ipProtocolOffset = 0;
-        int ikeOffset = 0;
-        if (isIpv6(pkt)) {
-            // IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
-            ipProtocolOffset = IP6_PROTO_OFFSET;
-            ikeOffset = IP6_HDRLEN + UDP_HDRLEN;
-        } else {
-            // Use default IPv4 header length (assuming no options)
-            ipProtocolOffset = IP4_PROTO_OFFSET;
-            ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
-
-            if (expectedUseEncap) {
-                if (hasNonEspMarker(pkt)) {
-                    ikeOffset += NON_ESP_MARKER_LEN;
-                } else {
-                    return false;
-                }
-            }
-        }
+        int ipProtocolOffset = isIpv6(pkt) ? IP6_PROTO_OFFSET : IP4_PROTO_OFFSET;
+        int ikeOffset = getIkeOffset(pkt, expectedUseEncap);
 
         return pkt[ipProtocolOffset] == IPPROTO_UDP
-                && areSpiAndMsgIdEqual(
+                && expectedUseEncap == hasNonEspMarker(pkt)
+                && isExpectedSpiAndMsgId(
                         pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp);
     }
 
+    private static boolean isExpectedIkeFragPkt(
+            byte[] pkt,
+            long expectedInitIkeSpi,
+            int expectedMsgId,
+            boolean expectedResp,
+            boolean expectedUseEncap,
+            int expectedFragNum) {
+        return isExpectedIkePkt(
+                        pkt, expectedInitIkeSpi, expectedMsgId, expectedResp, expectedUseEncap)
+                && isExpectedFragNum(pkt, getIkeOffset(pkt, expectedUseEncap), expectedFragNum);
+    }
+
+    private static int getIkeOffset(byte[] pkt, boolean useEncap) {
+        if (isIpv6(pkt)) {
+            // IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
+            return IP6_HDRLEN + UDP_HDRLEN;
+        } else {
+            // Use default IPv4 header length (assuming no options)
+            int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
+            return useEncap ? ikeOffset + NON_ESP_MARKER_LEN : ikeOffset;
+        }
+    }
+
     private static boolean hasNonEspMarker(byte[] pkt) {
         ByteBuffer buffer = ByteBuffer.wrap(pkt);
         int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
@@ -170,23 +244,81 @@
         return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
     }
 
-    private static boolean areSpiAndMsgIdEqual(
+    private static boolean isExpectedSpiAndMsgId(
             byte[] pkt,
             int ikeOffset,
-            long expectedIkeInitSpi,
+            long expectedInitIkeSpi,
             int expectedMsgId,
             boolean expectedResp) {
         if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false;
 
         ByteBuffer buffer = ByteBuffer.wrap(pkt);
         buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER)
+        buffer.mark(); // Mark this position so that later we can reset back here
 
-        // Check message ID.
+        // Check SPI
+        buffer.get(new byte[IKE_INIT_SPI_OFFSET]);
+        long initSpi = buffer.getLong();
+        if (expectedInitIkeSpi != initSpi) {
+            return false;
+        }
+
+        // Check direction
+        buffer.reset();
+        buffer.get(new byte[IKE_IS_RESP_BYTE_OFFSET]);
+        byte flagsByte = buffer.get();
+        boolean isResp = ((flagsByte & RSP_FLAG_MASK) != 0);
+        if (expectedResp != isResp) {
+            return false;
+        }
+
+        // Check message ID
+        buffer.reset();
         buffer.get(new byte[IKE_MSG_ID_OFFSET]);
-        int msgId = buffer.getInt();
-        return expectedMsgId == msgId;
 
-        // TODO: Check SPI and packet direction
+        // Both the expected message ID and the packet's msgId are signed integers, so directly
+        // compare them.
+        int msgId = buffer.getInt();
+        if (expectedMsgId != msgId) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private static boolean isExpectedFragNum(byte[] pkt, int ikeOffset, int expectedFragNum) {
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        buffer.get(new byte[ikeOffset]);
+        buffer.mark(); // Mark this position so that later we can reset back here
+
+        // Check if it is a fragment packet
+        buffer.get(new byte[IKE_FIRST_PAYLOAD_OFFSET]);
+        int firstPayload = Byte.toUnsignedInt(buffer.get());
+        if (firstPayload != IKE_PAYLOAD_TYPE_SKF) {
+            return false;
+        }
+
+        // Check fragment number
+        buffer.reset();
+        buffer.get(new byte[IKE_FRAG_NUM_OFFSET]);
+        int fragNum = Short.toUnsignedInt(buffer.getShort());
+        return expectedFragNum == fragNum;
+    }
+
+    public static class PortPair {
+        public final int srcPort;
+        public final int dstPort;
+
+        public PortPair(int sourcePort, int destinationPort) {
+            srcPort = sourcePort;
+            dstPort = destinationPort;
+        }
+    }
+
+    public static PortPair getSrcDestPortPair(byte[] outboundIkePkt) throws Exception {
+        return new PortPair(
+                getPort(outboundIkePkt, true /* shouldGetSource */),
+                getPort(outboundIkePkt, false /* shouldGetSource */));
     }
 
     private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception {
@@ -210,7 +342,7 @@
         return Short.toUnsignedInt(buffer.getShort());
     }
 
-    private static byte[] buildIkePacket(
+    public static byte[] buildIkePacket(
             InetAddress srcAddr,
             InetAddress dstAddr,
             int srcPort,
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
index cb1d826..5539dbc 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
@@ -47,7 +47,7 @@
     private static final String TAG = TunUtils.class.getSimpleName();
 
     private static final int DATA_BUFFER_LEN = 4096;
-    static final int TIMEOUT = 100;
+    static final int TIMEOUT = 500;
 
     static final int IP4_PROTO_OFFSET = 9;
     static final int IP6_PROTO_OFFSET = 6;
diff --git a/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt b/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt
new file mode 100644
index 0000000..40d0ca6
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2020 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.net.cts
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.Manifest.permission.NETWORK_SETTINGS
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.EthernetManager
+import android.net.InetAddresses
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
+import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkRequest
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
+import android.net.Uri
+import android.net.dhcp.DhcpDiscoverPacket
+import android.net.dhcp.DhcpPacket
+import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE
+import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_DISCOVER
+import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_REQUEST
+import android.net.dhcp.DhcpRequestPacket
+import android.net.shared.Inet4AddressUtils.getBroadcastAddress
+import android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address
+import android.os.Build
+import android.os.HandlerThread
+import android.platform.test.annotations.AppModeFull
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.ThrowingRunnable
+import com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DhcpClientPacketFilter
+import com.android.testutils.DhcpOptionFilter
+import com.android.testutils.RecorderCallback.CallbackEntry
+import com.android.testutils.TapPacketReader
+import com.android.testutils.TestableNetworkCallback
+import fi.iki.elonen.NanoHTTPD
+import org.junit.After
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.net.Inet4Address
+import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.TimeUnit
+import kotlin.reflect.KClass
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+private const val MAX_PACKET_LENGTH = 1500
+private const val TEST_TIMEOUT_MS = 10_000L
+
+private const val TEST_LEASE_TIMEOUT_SECS = 3600 * 12
+private const val TEST_PREFIX_LENGTH = 24
+
+private const val TEST_LOGIN_URL = "https://login.capport.android.com"
+private const val TEST_VENUE_INFO_URL = "https://venueinfo.capport.android.com"
+private const val TEST_DOMAIN_NAME = "lan"
+private const val TEST_MTU = 1500.toShort()
+
+@AppModeFull(reason = "Instant apps cannot create test networks")
+@RunWith(AndroidJUnit4::class)
+class CaptivePortalApiTest {
+    @JvmField
+    @Rule
+    val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
+
+    private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+    private val tnm by lazy { context.assertHasService(TestNetworkManager::class.java) }
+    private val eth by lazy { context.assertHasService(EthernetManager::class.java) }
+    private val cm by lazy { context.assertHasService(ConnectivityManager::class.java) }
+
+    private val handlerThread = HandlerThread(CaptivePortalApiTest::class.simpleName)
+    private val serverIpAddr = InetAddresses.parseNumericAddress("192.0.2.222") as Inet4Address
+    private val clientIpAddr = InetAddresses.parseNumericAddress("192.0.2.111") as Inet4Address
+    private val httpServer = HttpServer()
+    private val ethRequest = NetworkRequest.Builder()
+            // ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
+            .removeCapability(NET_CAPABILITY_TRUSTED)
+            .addTransportType(TRANSPORT_ETHERNET).build()
+    private val ethRequestCb = TestableNetworkCallback()
+
+    private lateinit var iface: TestNetworkInterface
+    private lateinit var reader: TapPacketReader
+    private lateinit var capportUrl: Uri
+
+    private var testSkipped = false
+
+    @Before
+    fun setUp() {
+        // This test requires using a tap interface as the default ethernet interface: skip if there
+        // is already an ethernet interface connected.
+        testSkipped = eth.isAvailable()
+        assumeFalse(testSkipped)
+
+        // Register a request so the network does not get torn down
+        cm.requestNetwork(ethRequest, ethRequestCb)
+        runAsShell(NETWORK_SETTINGS, MANAGE_TEST_NETWORKS) {
+            eth.setIncludeTestInterfaces(true)
+            // Keeping a reference to the test interface also makes sure the ParcelFileDescriptor
+            // does not go out of scope, which would cause it to close the underlying FileDescriptor
+            // in its finalizer.
+            iface = tnm.createTapInterface()
+        }
+
+        handlerThread.start()
+        reader = TapPacketReader(
+                handlerThread.threadHandler,
+                iface.fileDescriptor.fileDescriptor,
+                MAX_PACKET_LENGTH)
+        handlerThread.threadHandler.post { reader.start() }
+        httpServer.start()
+
+        // Pad the listening port to make sure it is always of length 5. This ensures the URL has
+        // always the same length so the test can use constant IP and UDP header lengths.
+        // The maximum port number is 65535 so a length of 5 is always enough.
+        capportUrl = Uri.parse("http://localhost:${httpServer.listeningPort}/testapi.html?par=val")
+    }
+
+    @After
+    fun tearDown() {
+        if (testSkipped) return
+        cm.unregisterNetworkCallback(ethRequestCb)
+
+        runAsShell(NETWORK_SETTINGS) { eth.setIncludeTestInterfaces(false) }
+
+        httpServer.stop()
+        handlerThread.threadHandler.post { reader.stop() }
+        handlerThread.quitSafely()
+
+        iface.fileDescriptor.close()
+    }
+
+    @Test
+    fun testApiCallbacks() {
+        // Handle the DHCP handshake that includes the capport API URL
+        val discover = reader.assertDhcpPacketReceived(
+                DhcpDiscoverPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_DISCOVER)
+        reader.sendResponse(makeOfferPacket(discover.clientMac, discover.transactionId))
+
+        val request = reader.assertDhcpPacketReceived(
+                DhcpRequestPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_REQUEST)
+        assertEquals(discover.transactionId, request.transactionId)
+        assertEquals(clientIpAddr, request.mRequestedIp)
+        reader.sendResponse(makeAckPacket(request.clientMac, request.transactionId))
+
+        // Expect a request to the capport API
+        val capportReq = httpServer.recordedRequests.poll(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+        assertNotNull(capportReq, "The device did not fetch captive portal API data within timeout")
+        assertEquals(capportUrl.path, capportReq.uri)
+        assertEquals(capportUrl.query, capportReq.queryParameterString)
+
+        // Expect network callbacks with capport info
+        val testCb = TestableNetworkCallback(TEST_TIMEOUT_MS)
+        // LinkProperties do not contain captive portal info if the callback is registered without
+        // NETWORK_SETTINGS permissions.
+        val lp = runAsShell(NETWORK_SETTINGS) {
+            cm.registerNetworkCallback(ethRequest, testCb)
+
+            try {
+                val ncCb = testCb.eventuallyExpect<CallbackEntry.CapabilitiesChanged> {
+                    it.caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
+                }
+                testCb.eventuallyExpect<CallbackEntry.LinkPropertiesChanged> {
+                    it.network == ncCb.network && it.lp.captivePortalData != null
+                }.lp
+            } finally {
+                cm.unregisterNetworkCallback(testCb)
+            }
+        }
+
+        assertEquals(capportUrl, lp.captivePortalApiUrl)
+        with(lp.captivePortalData) {
+            assertNotNull(this)
+            assertTrue(isCaptive)
+            assertEquals(Uri.parse(TEST_LOGIN_URL), userPortalUrl)
+            assertEquals(Uri.parse(TEST_VENUE_INFO_URL), venueInfoUrl)
+        }
+    }
+
+    private fun makeOfferPacket(clientMac: ByteArray, transactionId: Int) =
+            DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, transactionId,
+                    false /* broadcast */, serverIpAddr, IPV4_ADDR_ANY /* relayIp */, clientIpAddr,
+                    clientMac, TEST_LEASE_TIMEOUT_SECS,
+                    getPrefixMaskAsInet4Address(TEST_PREFIX_LENGTH),
+                    getBroadcastAddress(clientIpAddr, TEST_PREFIX_LENGTH),
+                    listOf(serverIpAddr) /* gateways */, listOf(serverIpAddr) /* dnsServers */,
+                    serverIpAddr, TEST_DOMAIN_NAME, null /* hostname */, true /* metered */,
+                    TEST_MTU, capportUrl.toString())
+
+    private fun makeAckPacket(clientMac: ByteArray, transactionId: Int) =
+            DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, transactionId,
+                    false /* broadcast */, serverIpAddr, IPV4_ADDR_ANY /* relayIp */, clientIpAddr,
+                    clientIpAddr /* requestClientIp */, clientMac, TEST_LEASE_TIMEOUT_SECS,
+                    getPrefixMaskAsInet4Address(TEST_PREFIX_LENGTH),
+                    getBroadcastAddress(clientIpAddr, TEST_PREFIX_LENGTH),
+                    listOf(serverIpAddr) /* gateways */, listOf(serverIpAddr) /* dnsServers */,
+                    serverIpAddr, TEST_DOMAIN_NAME, null /* hostname */, true /* metered */,
+                    TEST_MTU, false /* rapidCommit */, capportUrl.toString())
+
+    private fun parseDhcpPacket(bytes: ByteArray) = DhcpPacket.decodeFullPacket(
+            bytes, MAX_PACKET_LENGTH, DhcpPacket.ENCAP_L2)
+}
+
+/**
+ * A minimal HTTP server running on localhost (loopback), on a random available port.
+ *
+ * The server records each request in [recordedRequests] and will not serve any further request
+ * until the last one is removed from the queue for verification.
+ */
+private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
+    val recordedRequests = ArrayBlockingQueue<IHTTPSession>(1 /* capacity */)
+
+    override fun serve(session: IHTTPSession): Response {
+        recordedRequests.offer(session)
+        return newFixedLengthResponse("""
+                |{
+                |  "captive": true,
+                |  "user-portal-url": "$TEST_LOGIN_URL",
+                |  "venue-info-url": "$TEST_VENUE_INFO_URL"
+                |}
+            """.trimMargin())
+    }
+}
+
+private fun <T : DhcpPacket> TapPacketReader.assertDhcpPacketReceived(
+    packetType: KClass<T>,
+    timeoutMs: Long,
+    type: Byte
+): T {
+    val packetBytes = popPacket(timeoutMs, DhcpClientPacketFilter()
+            .and(DhcpOptionFilter(DHCP_MESSAGE_TYPE, type)))
+            ?: fail("${packetType.simpleName} not received within timeout")
+    val packet = DhcpPacket.decodeFullPacket(packetBytes, packetBytes.size, DhcpPacket.ENCAP_L2)
+    assertTrue(packetType.isInstance(packet),
+            "Expected ${packetType.simpleName} but got ${packet.javaClass.simpleName}")
+    return packetType.java.cast(packet)
+}
+
+private fun <T> Context.assertHasService(manager: Class<T>): T {
+    return getSystemService(manager) ?: fail("Service $manager not found")
+}
+
+/**
+ * Wrapper around runWithShellPermissionIdentity with kotlin-like syntax.
+ */
+private fun <T> runAsShell(vararg permissions: String, task: () -> T): T {
+    var ret: T? = null
+    runWithShellPermissionIdentity(ThrowingRunnable { ret = task() }, *permissions)
+    return ret ?: fail("ThrowingRunnable was not run")
+}
diff --git a/tests/tests/net/src/android/net/cts/NetworkStatsBinderTest.java b/tests/tests/net/src/android/net/cts/NetworkStatsBinderTest.java
index 1f3162f..1a48983 100644
--- a/tests/tests/net/src/android/net/cts/NetworkStatsBinderTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkStatsBinderTest.java
@@ -18,12 +18,15 @@
 
 import static android.os.Process.INVALID_UID;
 
+import static org.junit.Assert.assertEquals;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.INetworkStatsService;
 import android.net.TrafficStats;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -31,8 +34,15 @@
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.CollectionUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -41,17 +51,22 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-public class NetworkStatsBinderTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class NetworkStatsBinderTest {
     // NOTE: These are shamelessly copied from TrafficStats.
     private static final int TYPE_RX_BYTES = 0;
     private static final int TYPE_RX_PACKETS = 1;
     private static final int TYPE_TX_BYTES = 2;
     private static final int TYPE_TX_PACKETS = 3;
 
+    @Rule
+    public DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(
+            Build.VERSION_CODES.Q /* ignoreClassUpTo */);
+
     private final SparseArray<Function<Integer, Long>> mUidStatsQueryOpArray = new SparseArray<>();
 
-    @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         mUidStatsQueryOpArray.put(TYPE_RX_BYTES, uid -> TrafficStats.getUidRxBytes(uid));
         mUidStatsQueryOpArray.put(TYPE_RX_PACKETS, uid -> TrafficStats.getUidRxPackets(uid));
         mUidStatsQueryOpArray.put(TYPE_TX_BYTES, uid -> TrafficStats.getUidTxBytes(uid));
@@ -75,6 +90,7 @@
         return INVALID_UID;
     }
 
+    @Test
     public void testAccessUidStatsFromBinder() throws Exception {
         final int myUid = Process.myUid();
         final List<Integer> testUidList = new ArrayList<>();
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
index 37d7c57..4318f7f 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
@@ -238,6 +238,7 @@
             mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
             mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
 
+            // verify the relative ordering changed
             int newRank1 = out1.getRank();
             int newRank2 = out2.getRank();
             if (currentRank1 > currentRank2) {
@@ -415,8 +416,14 @@
         mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
         mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
 
-        assertEquals(currentRank1, out1.getRank());
-        assertEquals(currentRank2, out2.getRank());
+        // verify the relative ordering remains the same
+        int newRank1 = out1.getRank();
+        int newRank2 = out2.getRank();
+        if (currentRank1 > currentRank2) {
+            assertTrue(newRank1 > newRank2);
+        } else {
+            assertTrue(newRank1 < newRank2);
+        }
     }
 
     @Test
diff --git a/tests/tests/soundtrigger/Android.bp b/tests/tests/soundtrigger/Android.bp
new file mode 100644
index 0000000..6910adb
--- /dev/null
+++ b/tests/tests/soundtrigger/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 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: "CtsSoundTriggerTestCases",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+        "androidx.test.ext.junit",
+    ],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
diff --git a/tests/tests/soundtrigger/AndroidManifest.xml b/tests/tests/soundtrigger/AndroidManifest.xml
new file mode 100644
index 0000000..cfdc69f
--- /dev/null
+++ b/tests/tests/soundtrigger/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.soundtrigger.cts">
+
+    <application>
+      <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.soundtrigger.cts"
+                     android:label="CTS tests of android.soundtrigger">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/soundtrigger/AndroidTest.xml b/tests/tests/soundtrigger/AndroidTest.xml
new file mode 100644
index 0000000..f6df62c
--- /dev/null
+++ b/tests/tests/soundtrigger/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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 Voice Interaction 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_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="not-shardable" value="true" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSoundTriggerTestCases.apk"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.soundtrigger.cts" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/soundtrigger/OWNERS b/tests/tests/soundtrigger/OWNERS
new file mode 100644
index 0000000..20c9447
--- /dev/null
+++ b/tests/tests/soundtrigger/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 48436
+elaurent@google.com
+ytai@google.com
+nambur@google.com
diff --git a/tests/tests/soundtrigger/src/android/soundtrigger/cts/SoundTriggerTest.java b/tests/tests/soundtrigger/src/android/soundtrigger/cts/SoundTriggerTest.java
new file mode 100644
index 0000000..92d3b83
--- /dev/null
+++ b/tests/tests/soundtrigger/src/android/soundtrigger/cts/SoundTriggerTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2020 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.soundtrigger.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.soundtrigger.SoundTrigger;
+import android.media.AudioFormat;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+import java.util.Random;
+import java.util.UUID;
+
+@RunWith(AndroidJUnit4.class)
+public class SoundTriggerTest {
+    private static final int TEST_KEYPHRASE_ID = 200;
+    private static final String TEST_KEYPHRASE_TEXT = "test_keyphrase";
+    private static final int[] TEST_SUPPORTED_USERS = new int[] {1, 2, 3};
+    private static final int TEST_RECOGNITION_MODES = SoundTrigger.RECOGNITION_MODE_GENERIC
+            | SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION
+            | SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION
+            | SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+
+    private static final UUID TEST_MODEL_UUID = UUID.randomUUID();
+    private static final UUID TEST_VENDOR_UUID = UUID.randomUUID();
+    private static final int TEST_MODEL_VERSION = 123456;
+
+    private static final int TEST_MODULE_ID = 1;
+    private static final String TEST_IMPLEMENTOR = "test implementor";
+    private static final String TEST_DESCRIPTION = "test description";
+    private static final UUID TEST_MODULE_UUID = UUID.randomUUID();
+    private static final int TEST_MODULE_VERSION = 45678;
+    private static final String TEST_SUPPORTED_MODEL_ARCH = UUID.randomUUID().toString();
+    private static final int TEST_MAX_SOUND_MODELS = 10;
+    private static final int TEST_MAX_KEYPHRASES = 2;
+    private static final int TEST_MAX_USERS = 3;
+    private static final boolean TEST_SUPPORT_CAPTURE_TRANSITION = true;
+    private static final int TEST_MAX_BUFFER_SIZE = 2048;
+    private static final boolean TEST_SUPPORTS_CONCURRENT_CAPTURE = true;
+    private static final int TEST_POWER_CONSUMPTION_MW = 50;
+    private static final boolean TEST_RETURNES_TRIGGER_IN_EVENT = false;
+    private static final int TEST_AUDIO_CAPABILITIES =
+            SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
+                    | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+    private static final byte[] TEST_MODEL_DATA = new byte[1024];
+
+    @BeforeClass
+    public static void setUpClass() {
+        new Random().nextBytes(TEST_MODEL_DATA);
+    }
+
+    private static SoundTrigger.Keyphrase createTestKeyphrase() {
+        return new SoundTrigger.Keyphrase(TEST_KEYPHRASE_ID, TEST_RECOGNITION_MODES,
+                Locale.forLanguageTag("en-US"), TEST_KEYPHRASE_TEXT, TEST_SUPPORTED_USERS);
+    }
+
+    private static void verifyKeyphraseMatchesTestParams(SoundTrigger.Keyphrase keyphrase) {
+        assertEquals(keyphrase.getId(), TEST_KEYPHRASE_ID);
+        assertEquals(keyphrase.getRecognitionModes(), TEST_RECOGNITION_MODES);
+        assertEquals(keyphrase.getLocale(), Locale.forLanguageTag("en-US"));
+        assertEquals(keyphrase.getText(), TEST_KEYPHRASE_TEXT);
+        assertArrayEquals(keyphrase.getUsers(), TEST_SUPPORTED_USERS);
+    }
+
+    private static SoundTrigger.KeyphraseSoundModel createTestKeyphraseSoundModel() {
+        return new SoundTrigger.KeyphraseSoundModel(TEST_MODEL_UUID, TEST_VENDOR_UUID,
+                SoundTriggerTest.TEST_MODEL_DATA,
+                new SoundTrigger.Keyphrase[] {createTestKeyphrase()}, TEST_MODEL_VERSION);
+    }
+
+    private static void verifyKeyphraseSoundModelMatchesTestParams(
+            SoundTrigger.KeyphraseSoundModel keyphraseSoundModel) {
+        assertEquals(keyphraseSoundModel.getUuid(), TEST_MODEL_UUID);
+        assertEquals(keyphraseSoundModel.getVendorUuid(), TEST_VENDOR_UUID);
+        assertArrayEquals(keyphraseSoundModel.getData(), SoundTriggerTest.TEST_MODEL_DATA);
+        assertArrayEquals(keyphraseSoundModel.getKeyphrases(),
+                new SoundTrigger.Keyphrase[] {createTestKeyphrase()});
+        assertEquals(keyphraseSoundModel.getVersion(), TEST_MODEL_VERSION);
+        assertEquals(keyphraseSoundModel.getType(), SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+    }
+
+    private SoundTrigger.ModuleProperties createTestModuleProperties() {
+        return new SoundTrigger.ModuleProperties(TEST_MODULE_ID, TEST_IMPLEMENTOR, TEST_DESCRIPTION,
+                TEST_MODULE_UUID.toString(), TEST_MODULE_VERSION, TEST_SUPPORTED_MODEL_ARCH,
+                TEST_MAX_SOUND_MODELS, TEST_MAX_KEYPHRASES, TEST_MAX_USERS, TEST_RECOGNITION_MODES,
+                TEST_SUPPORT_CAPTURE_TRANSITION, TEST_MAX_BUFFER_SIZE,
+                TEST_SUPPORTS_CONCURRENT_CAPTURE, TEST_POWER_CONSUMPTION_MW,
+                TEST_RETURNES_TRIGGER_IN_EVENT, TEST_AUDIO_CAPABILITIES);
+    }
+
+    private static void verifyModulePropertiesMatchesTestParams(
+            SoundTrigger.ModuleProperties moduleProperties) {
+        assertEquals(moduleProperties.getId(), TEST_MODULE_ID);
+        assertEquals(moduleProperties.getImplementor(), TEST_IMPLEMENTOR);
+        assertEquals(moduleProperties.getDescription(), TEST_DESCRIPTION);
+        assertEquals(moduleProperties.getUuid(), TEST_MODULE_UUID);
+        assertEquals(moduleProperties.getVersion(), TEST_MODULE_VERSION);
+        assertEquals(moduleProperties.getSupportedModelArch(), TEST_SUPPORTED_MODEL_ARCH);
+        assertEquals(moduleProperties.getMaxSoundModels(), TEST_MAX_SOUND_MODELS);
+        assertEquals(moduleProperties.getMaxKeyphrases(), TEST_MAX_KEYPHRASES);
+        assertEquals(moduleProperties.getMaxUsers(), TEST_MAX_USERS);
+        assertEquals(moduleProperties.getRecognitionModes(), TEST_RECOGNITION_MODES);
+        assertEquals(moduleProperties.isCaptureTransitionSupported(),
+                TEST_SUPPORT_CAPTURE_TRANSITION);
+        assertEquals(moduleProperties.getMaxBufferMillis(), TEST_MAX_BUFFER_SIZE);
+        assertEquals(moduleProperties.isConcurrentCaptureSupported(),
+                TEST_SUPPORTS_CONCURRENT_CAPTURE);
+        assertEquals(moduleProperties.getPowerConsumptionMw(), TEST_POWER_CONSUMPTION_MW);
+        assertEquals(moduleProperties.isTriggerReturnedInEvent(), TEST_RETURNES_TRIGGER_IN_EVENT);
+        assertEquals(moduleProperties.getAudioCapabilities(), TEST_AUDIO_CAPABILITIES);
+        assertEquals(moduleProperties.describeContents(), 0);
+    }
+
+    @Test
+    public void testKeyphraseParcelUnparcel() {
+        SoundTrigger.Keyphrase keyphraseSrc = createTestKeyphrase();
+        verifyKeyphraseMatchesTestParams(keyphraseSrc);
+        Parcel parcel = Parcel.obtain();
+        keyphraseSrc.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        SoundTrigger.Keyphrase keyphraseResult = SoundTrigger.Keyphrase.readFromParcel(parcel);
+        assertEquals(keyphraseSrc, keyphraseResult);
+        verifyKeyphraseMatchesTestParams(keyphraseResult);
+
+        parcel.setDataPosition(0);
+        keyphraseResult = SoundTrigger.Keyphrase.CREATOR.createFromParcel(parcel);
+        assertEquals(keyphraseSrc, keyphraseResult);
+        verifyKeyphraseMatchesTestParams(keyphraseResult);
+    }
+
+    @Test
+    public void testKeyphraseSoundModelParcelUnparcel() {
+        SoundTrigger.KeyphraseSoundModel keyphraseSoundModelSrc =
+                createTestKeyphraseSoundModel();
+        Parcel parcel = Parcel.obtain();
+        keyphraseSoundModelSrc.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        SoundTrigger.KeyphraseSoundModel keyphraseSoundModelResult =
+                SoundTrigger.KeyphraseSoundModel.readFromParcel(parcel);
+        assertEquals(keyphraseSoundModelSrc, keyphraseSoundModelResult);
+        verifyKeyphraseSoundModelMatchesTestParams(keyphraseSoundModelResult);
+
+        parcel.setDataPosition(0);
+        keyphraseSoundModelResult = SoundTrigger.KeyphraseSoundModel.CREATOR.createFromParcel(
+                parcel);
+        assertEquals(keyphraseSoundModelSrc, keyphraseSoundModelResult);
+        verifyKeyphraseSoundModelMatchesTestParams(keyphraseSoundModelResult);
+    }
+
+    @Test
+    public void testModulePropertiesParcelUnparcel() {
+        SoundTrigger.ModuleProperties modulePropertiesSrc = createTestModuleProperties();
+        Parcel parcel = Parcel.obtain();
+        modulePropertiesSrc.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        SoundTrigger.ModuleProperties modulePropertiesResult =
+                SoundTrigger.ModuleProperties.CREATOR.createFromParcel(parcel);
+        assertEquals(modulePropertiesSrc, modulePropertiesResult);
+        verifyModulePropertiesMatchesTestParams(modulePropertiesResult);
+    }
+
+    @Test
+    public void testModelParamRangeParcelUnparcel() {
+        SoundTrigger.ModelParamRange modelParamRangeSrc = new SoundTrigger.ModelParamRange(-1, 10);
+        Parcel parcel = Parcel.obtain();
+        modelParamRangeSrc.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        SoundTrigger.ModelParamRange modelParamRangeResult =
+                SoundTrigger.ModelParamRange.CREATOR.createFromParcel(parcel);
+        assertEquals(modelParamRangeSrc, modelParamRangeResult);
+        assertEquals(modelParamRangeResult.getStart(), -1);
+        assertEquals(modelParamRangeResult.getEnd(), 10);
+    }
+
+    @Test
+    public void testRecognitionEventBasicGetters() {
+        AudioFormat audioFormat = new AudioFormat.Builder().build();
+        SoundTrigger.RecognitionEvent recognitionEvent = new SoundTrigger.RecognitionEvent(
+                0, 100, true, 101, 1000, 1001, true, audioFormat, TEST_MODEL_DATA);
+        assertEquals(recognitionEvent.getCaptureFormat(), audioFormat);
+        assertEquals(recognitionEvent.getCaptureSession(), 101);
+        assertArrayEquals(recognitionEvent.getData(), TEST_MODEL_DATA);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastDataMigrationTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastDataMigrationTest.java
new file mode 100644
index 0000000..3be25e6
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastDataMigrationTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.provider.Telephony.CellBroadcasts;
+import android.util.Log;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import androidx.test.InstrumentationRegistry;
+
+public class CellBroadcastDataMigrationTest {
+    private static final String TAG = CellBroadcastDataMigrationTest.class.getSimpleName();
+    /**
+     * To support data migration when upgrading from an older device, device need to define
+     * legacy content provider. This tests verify that the legacy cellbroadcast contentprovider
+     * only surfaces data for migration. The data should be protected by proper permissions and
+     * it should be headless without any other activities, services or providers to handle alerts.
+     */
+    @Test
+    public void testLegacyContentProvider() throws Exception {
+        final ProviderInfo legacy = InstrumentationRegistry.getContext().getPackageManager()
+                .resolveContentProvider(CellBroadcasts.AUTHORITY_LEGACY, 0);
+        if (legacy == null) {
+            Log.d(TAG, "Device does not support data migration");
+            return;
+        }
+
+        // Verify that legacy provider is protected with certain permissions
+        assertEquals("Legacy provider at MediaStore.AUTHORITY_LEGACY must protect its data",
+                android.Manifest.permission.READ_CELL_BROADCASTS, legacy.readPermission);
+
+        // And finally verify that legacy provider is headless. We expect the legacy provider only
+        // surface the old data for migration rather than handling emergency alerts.
+        final PackageInfo legacyPackage = InstrumentationRegistry.getContext().getPackageManager()
+                .getPackageInfo(legacy.packageName, PackageManager.GET_ACTIVITIES
+                        | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS
+                        | PackageManager.GET_SERVICES);
+        assertEmpty("Headless LegacyCellBroadcastContentProvider must have no activities",
+                legacyPackage.activities);
+        assertEquals("Headless LegacyCellBroadcastContentProvider must have exactly one provider",
+                1, legacyPackage.providers.length);
+        assertEmpty("Headless LegacyCellBroadcastContentProvider must have no receivers",
+                legacyPackage.receivers);
+        assertEmpty("Headless LegacyCellBroadcastContentProvider must have no services",
+                legacyPackage.services);
+    }
+
+    private static <T> void assertEmpty(String message, T[] array) {
+        if (array != null && array.length > 0) {
+            fail(message);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IwlanModeTest.java b/tests/tests/telephony/current/src/android/telephony/cts/IwlanModeTest.java
new file mode 100644
index 0000000..3786e23
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IwlanModeTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class IwlanModeTest {
+    private TelephonyManager mTelephonyManager;
+
+    private PackageManager mPackageManager;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mPackageManager = mContext.getPackageManager();
+        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+    }
+
+    @Test
+    public void testIwlanMode() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        // All new devices should not enable IWLAN legacy mode. Legacy mode is for existing old
+        // devices.
+        if (mTelephonyManager.getRadioHalVersion().first >= 1
+                && mTelephonyManager.getRadioHalVersion().second >= 5) {
+            String mode = SystemProperties.get("ro.telephony.iwlan_operation_mode");
+            assertNotEquals("legacy", mode);
+
+            String wlanDataServicePackage = mContext.getResources().getString(
+                    com.android.internal.R.string.config_wlan_data_service_package);
+            assertNotEquals("", wlanDataServicePackage);
+            assertNotEquals("com.android.phone", wlanDataServicePackage);
+
+            String wlanNetworkServicePackage = mContext.getResources().getString(
+                    com.android.internal.R.string.config_wlan_network_service_package);
+            assertNotEquals("", wlanNetworkServicePackage);
+            assertNotEquals("com.android.phone", wlanNetworkServicePackage);
+
+        }
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index 2b4e3ac..80cfc02 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -318,6 +318,11 @@
 
     @Test
     public void testSetSupportedCountries() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
 
@@ -334,6 +339,11 @@
 
     @Test
     public void testSetUnsupportedCountries() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
 
@@ -350,6 +360,11 @@
 
     @Test
     public void testIsSupportedCountry_returnsTrue_ifCountryIsOnSupportedList() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
 
@@ -365,6 +380,11 @@
 
     @Test
     public void testIsSupportedCountry_returnsTrue_ifCountryIsNotOnUnsupportedList() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
         List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
@@ -384,6 +404,11 @@
 
     @Test
     public void testIsSupportedCountry_returnsFalse_ifCountryIsNotOnSupportedList() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
 
@@ -399,6 +424,11 @@
 
     @Test
     public void testIsSupportedCountry_returnsFalse_ifCountryIsOnUnsupportedList() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
         List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
@@ -418,6 +448,11 @@
 
     @Test
     public void testIsSupportedCountry_returnsFalse_ifBothListsAreEmpty() {
+        // Only test it when EuiccManager is enabled.
+        if (!mEuiccManager.isEnabled()) {
+            return;
+        }
+
         // Get country list for restoring later.
         List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
         List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
diff --git a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 8665c7e..1055531 100644
--- a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -56,6 +56,7 @@
 import android.net.TetheringManager.TetheringInterfaceRegexps;
 import android.net.TetheringManager.TetheringRequest;
 import android.net.cts.util.CtsNetUtils;
+import android.net.cts.util.CtsNetUtils.TestNetworkCallback;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.PersistableBundle;
@@ -714,7 +715,15 @@
                 mCtsNetUtils.disconnectFromWifi(null);
             }
 
-            final Network activeNetwork = mCm.getActiveNetwork();
+            final TestNetworkCallback networkCallback = new TestNetworkCallback();
+            Network activeNetwork = null;
+            try {
+                mCm.registerDefaultNetworkCallback(networkCallback);
+                activeNetwork = networkCallback.waitForAvailable();
+            } finally {
+                mCm.unregisterNetworkCallback(networkCallback);
+            }
+
             assertNotNull("No active network. Please ensure the device has working mobile data.",
                     activeNetwork);
             final NetworkCapabilities activeNetCap = mCm.getNetworkCapabilities(activeNetwork);
diff --git a/tests/tests/uirendering/Android.bp b/tests/tests/uirendering/Android.bp
index 99f04cf..ee9fa2b 100644
--- a/tests/tests/uirendering/Android.bp
+++ b/tests/tests/uirendering/Android.bp
@@ -24,6 +24,7 @@
     static_libs: [
         "compatibility-device-util-axt",
         "ctsdeviceutillegacy-axt",
+        "cts-wm-util",
         "mockito-target-minus-junit4",
         "androidx.test.rules",
         "kotlin-test",
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/runner/UiRenderingRunner.java b/tests/tests/uirendering/src/android/uirendering/cts/runner/UiRenderingRunner.java
index c161d49..072f501 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/runner/UiRenderingRunner.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/runner/UiRenderingRunner.java
@@ -18,6 +18,8 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.provider.Settings;
+import android.server.wm.settings.SettingsSession;
 import android.support.test.uiautomator.UiDevice;
 import android.uirendering.cts.util.BitmapDumper;
 
@@ -28,6 +30,16 @@
  */
 public class UiRenderingRunner extends AndroidJUnitRunner {
 
+    private static class ImmersiveConfirmationSetting extends SettingsSession<String> {
+        ImmersiveConfirmationSetting() {
+            super(Settings.Secure.getUriFor(
+                    Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS),
+                    Settings.Secure::getString, Settings.Secure::putString);
+        }
+    }
+
+    private ImmersiveConfirmationSetting mSettingsSession;
+
     @Override
     protected void waitForActivitiesToComplete() {
         // No.
@@ -37,6 +49,10 @@
     public void onCreate(Bundle arguments) {
         super.onCreate(arguments);
 
+        // Disable immersive clings
+        mSettingsSession = new ImmersiveConfirmationSetting();
+        mSettingsSession.set("confirmed");
+
         final UiDevice device = UiDevice.getInstance(this);
         try {
             device.wakeUp();
@@ -53,6 +69,8 @@
         // Ok now wait if necessary
         super.waitForActivitiesToComplete();
 
+        mSettingsSession.close();
+
         super.onDestroy();
     }
 }
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
index d5a0f3a..f57221c 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
@@ -84,19 +84,25 @@
         ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.setVerboseLoggingEnabled(true));
 
+        // enable Wifi
         if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled());
+
+        // turn screen on
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         turnScreenOn();
-        PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled());
+
+        // check we have >= 1 saved network
         List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.getConfiguredNetworks());
         assertWithMessage("Need at least one saved network").that(savedNetworks).isNotEmpty();
-        // Wait for wifi is to be connected
+
+        // ensure Wifi is connected
+        ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
         PollingCheck.check(
                 "Wifi not connected",
                 DURATION,
                 () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
-        assertThat(mWifiManager.getConnectionInfo().getNetworkId()).isNotEqualTo(-1);
     }
 
     @After
@@ -300,6 +306,7 @@
                 new TestConnectedNetworkScorer(countDownLatchScorer);
         TestUsabilityStatsListener usabilityStatsListener =
                 new TestUsabilityStatsListener(countDownLatchUsabilityStats);
+        boolean disconnected = false;
         try {
             uiAutomation.adoptShellPermissionIdentity();
             // Clear any external scorer already active on the device.
@@ -340,7 +347,7 @@
                     "Wifi not disconnected",
                     DURATION,
                     () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
-            assertThat(mWifiManager.getConnectionInfo().getNetworkId()).isEqualTo(-1);
+            disconnected = true;
 
             // Wait for stop to be invoked and ensure that the session id matches.
             assertThat(countDownLatchScorer.await(DURATION, TimeUnit.MILLISECONDS)).isTrue();
@@ -349,6 +356,16 @@
         } finally {
             mWifiManager.removeOnWifiUsabilityStatsListener(usabilityStatsListener);
             mWifiManager.clearWifiConnectedNetworkScorer();
+
+            if (disconnected) {
+                mWifiManager.reconnect();
+                // Wait for it to be reconnected.
+                PollingCheck.check(
+                        "Wifi not reconnected",
+                        DURATION,
+                        () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+            }
+
             uiAutomation.dropShellPermissionIdentity();
         }
     }
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
index d7e4a23..5271ef9 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
@@ -34,6 +34,7 @@
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 
+import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
 
@@ -130,10 +131,12 @@
 
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
-        if (!mWifiManager.isWifiEnabled())
-            setWifiEnabled(true);
-        Thread.sleep(ENABLE_WAIT_MSEC);
-        assertThat(mWifiManager.isWifiEnabled()).isTrue();
+
+        // enable Wifi
+        if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        PollingCheck.check("Wifi not enabled", ENABLE_WAIT_MSEC,
+                () -> mWifiManager.isWifiEnabled());
+
         mMySync.expectedState = STATE_NULL;
     }
 
@@ -299,10 +302,15 @@
             return;
         }
 
+        // ensure Wifi is connected
+        ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
+        PollingCheck.check(
+                "Wifi not connected",
+                ENABLE_WAIT_MSEC,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+
         final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
         assertThat(wifiInfo).isNotNull();
-        assertWithMessage("Wifi should be connected!")
-                .that(wifiInfo.getBSSID()).isNotNull();
 
         ScanResult currentNetwork = null;
         for (int i = 0; i < SCAN_FIND_BSSID_MAX_RETRY_COUNT; i++) {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiInfoTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiInfoTest.java
index 557710d..ed75592 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiInfoTest.java
@@ -31,6 +31,7 @@
 import android.test.AndroidTestCase;
 
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
 
 import java.nio.charset.StandardCharsets;
@@ -90,10 +91,11 @@
         assertThat(mWifiManager).isNotNull();
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
-        if (!mWifiManager.isWifiEnabled())
-            setWifiEnabled(true);
-        Thread.sleep(DURATION);
-        assertThat(mWifiManager.isWifiEnabled()).isTrue();
+
+        // enable Wifi
+        if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled());
+
         mMySync.expectedState = STATE_NULL;
     }
 
@@ -133,7 +135,8 @@
             return;
         }
 
-        // wait for Wifi to be connected
+        // ensure Wifi is connected
+        ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
         PollingCheck.check(
                 "Wifi not connected - Please ensure there is a saved network in range of this "
                         + "device",
@@ -147,20 +150,11 @@
 
         setWifiEnabled(false);
 
-        PollingCheck.check("getNetworkId not -1", 20000, new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-                return wifiInfo.getNetworkId() == -1;
-            }
-        });
+        PollingCheck.check("getNetworkId not -1", 20000,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
 
-        PollingCheck.check("getWifiState not disabled", 20000, new Callable<Boolean>() {
-           @Override
-            public Boolean call() throws Exception {
-               return mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED;
-            }
-        });
+        PollingCheck.check("getWifiState not disabled", 20000,
+                () -> mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED);
     }
 
     private void testWifiInfoPropertiesWhileConnected(WifiInfo wifiInfo) {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
index 0ca73fc..f0ffdcc 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
@@ -107,12 +107,17 @@
         ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.setScanThrottleEnabled(false));
 
+        // enable Wifi
         if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
         PollingCheck.check("Wifi not enabled", DURATION_MS, () -> mWifiManager.isWifiEnabled());
+
+        // check we have >= 1 saved network
         List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.getConfiguredNetworks());
         assertWithMessage("Need at least one saved network").that(savedNetworks).isNotEmpty();
-        // Wait for wifi is to be connected
+
+        // ensure Wifi is connected
+        ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
         PollingCheck.check(
                 "Wifi not connected",
                 DURATION_MS,
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 94fbf5b..f928ef6 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -293,12 +293,14 @@
 
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
-        if (!mWifiManager.isWifiEnabled())
-            setWifiEnabled(true);
+        // enable Wifi
+        if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        PollingCheck.check("Wifi not enabled", TEST_WAIT_DURATION_MS,
+                () -> mWifiManager.isWifiEnabled());
+
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         turnScreenOnNoDelay();
-        Thread.sleep(TEST_WAIT_DURATION_MS);
-        assertTrue(mWifiManager.isWifiEnabled());
+
         synchronized (mMySync) {
             mMySync.expectedState = STATE_NULL;
         }
@@ -1459,17 +1461,18 @@
     }
 
     private void verifyRegisterSoftApCallback(TestExecutor executor, TestSoftApCallback callback)
-            throws Exception{
+            throws Exception {
         // Register callback to get SoftApCapability
         mWifiManager.registerSoftApCallback(executor, callback);
         PollingCheck.check(
                 "SoftAp register failed!", 1_000,
-                () -> { executor.runAll();
-                // Verify callback is run on the supplied executor and called
-                return callback.getOnStateChangedCalled() &&
-                callback.getOnSoftapInfoChangedCalled() &&
-                callback.getOnSoftApCapabilityChangedCalled() &&
-                callback.getOnConnectedClientCalled();
+                () -> {
+                    executor.runAll();
+                    // Verify callback is run on the supplied executor and called
+                    return callback.getOnStateChangedCalled() &&
+                            callback.getOnSoftapInfoChangedCalled() &&
+                            callback.getOnSoftApCapabilityChangedCalled() &&
+                            callback.getOnConnectedClientCalled();
                 });
     }
 
@@ -1626,9 +1629,10 @@
             // Verify state and info callback value as expected
             PollingCheck.check(
                     "SoftAp channel and state mismatch!!!", 5_000,
-                    () -> { executor.runAll();
-                    return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState() &&
-                    2462 == callback.getCurrentSoftApInfo().getFrequency();
+                    () -> {
+                        executor.runAll();
+                        return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState() &&
+                                2462 == callback.getCurrentSoftApInfo().getFrequency();
                     });
 
             // stop tethering which used to verify stopSoftAp
@@ -1637,10 +1641,11 @@
             // Verify clean up
             PollingCheck.check(
                     "Stop Softap failed", 2_000,
-                    () -> { executor.runAll();
-                    return WifiManager.WIFI_AP_STATE_DISABLED == callback.getCurrentState() &&
-                    0 == callback.getCurrentSoftApInfo().getBandwidth() &&
-                    0 == callback.getCurrentSoftApInfo().getFrequency();
+                    () -> {
+                        executor.runAll();
+                        return WifiManager.WIFI_AP_STATE_DISABLED == callback.getCurrentState() &&
+                                0 == callback.getCurrentSoftApInfo().getBandwidth() &&
+                                0 == callback.getCurrentSoftApInfo().getFrequency();
                     });
         } finally {
             mWifiManager.unregisterSoftApCallback(callback);
@@ -1724,7 +1729,7 @@
             // Re-enable all saved networks before exiting.
             if (savedNetworks != null) {
                 for (WifiConfiguration network : savedNetworks) {
-                    mWifiManager.enableNetwork(network.networkId, false);
+                    mWifiManager.enableNetwork(network.networkId, true);
                 }
             }
             uiAutomation.dropShellPermissionIdentity();
@@ -2015,7 +2020,7 @@
             // re-enable disabled networks
             for (int disabledNetworkId : disabledNetworkIds) {
                 ShellIdentityUtils.invokeWithShellPermissions(
-                        () -> mWifiManager.enableNetwork(disabledNetworkId, false));
+                        () -> mWifiManager.enableNetwork(disabledNetworkId, true));
             }
         }
     }
@@ -2320,7 +2325,8 @@
             return;
         }
 
-        // wait for Wifi to be connected
+        // ensure Wifi is connected
+        ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.reconnect());
         PollingCheck.check(
                 "Wifi not connected - Please ensure there is a saved network in range of this "
                         + "device",
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
index ba55513..7670a52 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -102,14 +102,19 @@
         ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.setScanThrottleEnabled(false));
 
+        // enable Wifi
         if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        turnScreenOn();
         PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled());
 
+        // turn screen on
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        turnScreenOn();
+
+        // check we have >= 1 saved network
         List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
                 () -> mWifiManager.getPrivilegedConfiguredNetworks());
         assertFalse("Need at least one saved network", savedNetworks.isEmpty());
+
         // Pick any one of the saved networks on the device (assumes that it is in range)
         mTestNetwork = savedNetworks.get(0);
         // Disconnect & disable auto-join on the saved network to prevent auto-connect from
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index 4264d3f..b52d80d 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -145,11 +145,15 @@
 
     // Performance numbers only make sense on real devices, so skip on non-real devices
     public static boolean frankenDevice() throws IOException {
-        String systemBrand = getProperty("ro.product.system_ext.brand");
-        String systemModel = getProperty("ro.product.system_ext.model");
-        String systemProduct = getProperty("ro.product.system_ext.name");
-        if (("Android".equals(systemBrand) || "generic".equals(systemBrand)) &&
-            (systemModel.startsWith("AOSP on ") || systemProduct.startsWith("aosp_"))) {
+        String systemBrand = getProperty("ro.product.system.brand");
+        String systemModel = getProperty("ro.product.system.model");
+        String systemProduct = getProperty("ro.product.system.name");
+        // not all devices may have system_ext partition
+        String systemExtProduct = getProperty("ro.product.system_ext.name");
+        if (("Android".equals(systemBrand) || "generic".equals(systemBrand) ||
+                "mainline".equals(systemBrand)) &&
+            (systemModel.startsWith("AOSP on ") || systemProduct.startsWith("aosp_") ||
+                systemExtProduct.startsWith("aosp_"))) {
             return true;
         }
         return false;