summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-02-05 13:00:29 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-02-05 13:00:29 +0000
commiteb5f9c6be33d6fc717d6f7f61d66c2f26ae0ba47 (patch)
tree3a7f0ed5b670f0d99caf733c2f298c298d6bc485
parent49d5297c12d827fdd640cc05c71c672845506c43 (diff)
parenta5fd7706fc67110511362d5dacee26de4b1060d0 (diff)
downloadcts-android12-mainline-media-release.tar.gz
Snap for 8152310 from a5fd7706fc67110511362d5dacee26de4b1060d0 to mainline-media-releaseandroid-mainline-12.0.0_r89android12-mainline-media-release
Change-Id: Id7f08b5288431ea58bcea0a56d6a8cd15bca6084
-rw-r--r--hostsidetests/appcompat/strictjavapackages/Android.bp2
-rw-r--r--hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java3
-rw-r--r--tests/MediaProviderTranscode/Android.bp4
-rw-r--r--tests/PhotoPicker/Android.bp2
-rw-r--r--tests/PhotoPicker/AndroidTest.xml7
-rw-r--r--tests/PhotoPicker/res/raw/test_video.mp4bin0 -> 135632 bytes
-rw-r--r--tests/PhotoPicker/res/raw/test_video_dng.mp4 (renamed from tests/PhotoPicker/res/raw/testvideo_meta.mp4)bin20716 -> 20716 bytes
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java24
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java5
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java332
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java38
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java17
-rw-r--r--tests/providerui/AndroidManifest.xml4
-rw-r--r--tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java180
-rw-r--r--tests/tests/permission3/Android.bp1
-rw-r--r--tests/tests/permission3/AndroidTest.xml1
-rw-r--r--tests/tests/permission3/UsePermissionApp31/Android.bp32
-rw-r--r--tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml28
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt2
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt156
20 files changed, 703 insertions, 135 deletions
diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp
index fcb20e829cd..9ab8a832ca3 100644
--- a/hostsidetests/appcompat/strictjavapackages/Android.bp
+++ b/hostsidetests/appcompat/strictjavapackages/Android.bp
@@ -33,6 +33,6 @@ java_test_host {
test_suites: [
"cts",
"general-tests",
- "mts",
+ "mts-mainline-infra",
],
}
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
index b2dc9b816ac..e44a04ae2b0 100644
--- a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
@@ -41,6 +41,7 @@ import androidx.test.uiautomator.Until;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeNotNull;
/** Basic sample for unbundled UiAutomator. */
@RunWith(AndroidJUnit4.class)
@@ -111,7 +112,7 @@ public class DeviceTest {
mContext.startActivity(intent);
UiObject2 view = waitForView(By.text(Constants.TEST_APP_PACKAGE));
- assertNotNull("Activity under-test was not launched or found!", view);
+ assumeNotNull("Activity under-test was not launched or found!", view);
Log.d(LOG_TAG, "Started Activity under-test.");
}
diff --git a/tests/MediaProviderTranscode/Android.bp b/tests/MediaProviderTranscode/Android.bp
index 4ba3243e177..0a6dfd66ac8 100644
--- a/tests/MediaProviderTranscode/Android.bp
+++ b/tests/MediaProviderTranscode/Android.bp
@@ -7,6 +7,7 @@ android_test {
test_suites: [
"device-tests",
"cts",
+ "mts-mediaprovider",
],
compile_multilib: "both",
@@ -30,6 +31,7 @@ android_test {
"truth-prebuilt",
],
+ min_sdk_version: "30",
certificate: "media",
java_resources: [":CtsTranscodeTestAppSupportsHevc", ":CtsTranscodeTestAppSupportsSlowMotion"]
}
@@ -45,6 +47,7 @@ android_test_helper_app {
],
static_libs: ["androidx.legacy_legacy-support-v4"],
target_sdk_version: "28",
+ min_sdk_version: "30",
}
android_test_helper_app {
@@ -58,4 +61,5 @@ android_test_helper_app {
],
static_libs: ["androidx.legacy_legacy-support-v4"],
target_sdk_version: "28",
+ min_sdk_version: "30",
}
diff --git a/tests/PhotoPicker/Android.bp b/tests/PhotoPicker/Android.bp
index 37b049b36c2..5a9fc331708 100644
--- a/tests/PhotoPicker/Android.bp
+++ b/tests/PhotoPicker/Android.bp
@@ -22,7 +22,7 @@ android_test {
test_config: "AndroidTest.xml",
srcs: ["src/**/*.java", "helper/**/*.java", ":CtsProviderTestUtils",],
compile_multilib: "both",
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
sdk_version: "core_current",
min_sdk_version: "30",
libs: [
diff --git a/tests/PhotoPicker/AndroidTest.xml b/tests/PhotoPicker/AndroidTest.xml
index dbd1bc41e5c..d0bf7973c0b 100644
--- a/tests/PhotoPicker/AndroidTest.xml
+++ b/tests/PhotoPicker/AndroidTest.xml
@@ -20,9 +20,14 @@
<option name="test-file-name" value="CtsPhotoPickerTest.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- <option name="force-root" value="false" />
+ <option name="force-root" value="true" />
</target_preparer>
+ <option
+ name="config-descriptor:metadata"
+ key="mainline-param"
+ value="com.google.android.mediaprovider.apex" />
+
<option name="test-suite-tag" value="cts" />
<option name="test-tag" value="PhotoPickerTests" />
<!-- Instant apps cannot access external storage -->
diff --git a/tests/PhotoPicker/res/raw/test_video.mp4 b/tests/PhotoPicker/res/raw/test_video.mp4
new file mode 100644
index 00000000000..ab95ac07dd3
--- /dev/null
+++ b/tests/PhotoPicker/res/raw/test_video.mp4
Binary files differ
diff --git a/tests/PhotoPicker/res/raw/testvideo_meta.mp4 b/tests/PhotoPicker/res/raw/test_video_dng.mp4
index 9b38f0e8e7d..9b38f0e8e7d 100644
--- a/tests/PhotoPicker/res/raw/testvideo_meta.mp4
+++ b/tests/PhotoPicker/res/raw/test_video_dng.mp4
Binary files differ
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
index 49a1acbd772..5873feed179 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
@@ -25,6 +25,8 @@ import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Before;
/**
@@ -41,6 +43,7 @@ public class PhotoPickerBaseTest {
@Before
public void setUp() throws Exception {
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ mDevice = UiDevice.getInstance(inst);
enablePhotoPickerFlag(inst);
@@ -49,7 +52,6 @@ public class PhotoPickerBaseTest {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Wake up the device and dismiss the keyguard before the test starts
- mDevice = UiDevice.getInstance(inst);
mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
mDevice.executeShellCommand("wm dismiss-keyguard");
@@ -60,13 +62,17 @@ public class PhotoPickerBaseTest {
mDevice.waitForIdle();
}
- private void enablePhotoPickerFlag(Instrumentation inst) {
- inst.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.WRITE_DEVICE_CONFIG);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- "picker_intent_enabled" /* name */,
- "true" /* value */,
- false /* makeDefault */);
+ private void enablePhotoPickerFlag(Instrumentation inst) throws Exception {
+ if (SdkLevel.isAtLeastS()) {
+ inst.getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.WRITE_DEVICE_CONFIG);
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "picker_intent_enabled" /* name */,
+ "true" /* value */,
+ false /* makeDefault */);
+ } else {
+ mDevice.executeShellCommand("setprop persist.sys.storage_picker_enabled true");
+ }
}
}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
index 10587786202..c05d22e2d46 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
@@ -31,7 +31,6 @@ import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
-import androidx.test.filters.SdkSuppress;
import androidx.test.uiautomator.UiObject;
import com.android.bedstead.harrier.BedsteadJUnit4;
@@ -53,7 +52,6 @@ import java.util.List;
* Photo Picker Device only tests for cross profile interaction flows.
*/
@RunWith(BedsteadJUnit4.class)
-@SdkSuppress(minSdkVersion = 31, codeName = "S")
public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest {
@ClassRule @Rule
public static final DeviceState sDeviceState = new DeviceState();
@@ -70,7 +68,6 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest {
@Test
@RequireRunOnWorkProfile
- @Ignore("Enable after b/201323670 is fixed")
public void testWorkApp_canAccessPersonalProfileContents() throws Exception {
final int imageCount = 2;
createImages(imageCount, sDeviceState.primaryUser().id(), mUriList);
@@ -109,7 +106,7 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest {
@Test
@EnsureHasWorkProfile
- @Ignore("Enable after b/201323670 is fixed")
+ @Ignore("Enable after b/216475844 is fixed")
public void testPersonalApp_canAccessWorkProfileContents() throws Exception {
final int imageCount = 2;
createImages(imageCount, sDeviceState.workProfile().id(), mUriList);
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
index 9b5904cefde..8d26dd8abbc 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
@@ -19,11 +19,12 @@ package android.photopicker.cts;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideos;
import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages;
import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideos;
import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
-import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton;
@@ -38,7 +39,6 @@ import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
-import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiSelector;
@@ -54,7 +54,6 @@ import java.util.List;
* Photo Picker Device only tests for common flows.
*/
@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 31, codeName = "S")
public class PhotoPickerTest extends PhotoPickerBaseTest {
private List<Uri> mUriList = new ArrayList<>();
@@ -75,8 +74,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
mActivity.startActivityForResult(intent, REQUEST_CODE);
final UiObject item = findItemList(itemCount).get(0);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(item);
final Uri uri = mActivity.getResult().data.getData();
assertPickerUriFormat(uri, mContext.getUserId());
@@ -96,8 +94,8 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
mDevice.waitForIdle();
final UiObject addButton = findPreviewAddOrSelectButton();
- addButton.click();
- mDevice.waitForIdle();
+ assertThat(addButton.waitForExists(1000)).isTrue();
+ clickAndWait(addButton);
final Uri uri = mActivity.getResult().data.getData();
assertPickerUriFormat(uri, mContext.getUserId());
@@ -137,9 +135,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
assertThat(itemCount).isEqualTo(imageCount);
// Select maxCount + 1 item
for (int i = 0; i < itemCount; i++) {
- final UiObject item = itemList.get(i);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(i));
}
UiObject snackbarTextView = mDevice.findObject(new UiSelector().text(
@@ -150,9 +146,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
assertWithMessage("Timed out waiting for snackbar to disappear").that(
snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue();
- final UiObject addButton = findAddButton();
- addButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findAddButton());
final ClipData clipData = mActivity.getResult().data.getClipData();
final int count = clipData.getItemCount();
@@ -171,9 +165,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
final int itemCount = itemList.size();
assertThat(itemCount).isEqualTo(imageCount);
// Select 1 item
- final UiObject item = itemList.get(0);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(0));
final Uri uri = mActivity.getResult().data.getData();
assertPickerUriFormat(uri, mContext.getUserId());
@@ -193,14 +185,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
final int itemCount = itemList.size();
assertThat(itemCount).isEqualTo(imageCount);
for (int i = 0; i < itemCount; i++) {
- final UiObject item = itemList.get(i);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(i));
}
- final UiObject addButton = findAddButton();
- addButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findAddButton());
final ClipData clipData = mActivity.getResult().data.getClipData();
final int count = clipData.getItemCount();
@@ -215,7 +203,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
@Test
public void testMultiSelect_longPress() throws Exception {
final int videoCount = 3;
- createVideos(videoCount, mContext.getUserId(), mUriList);
+ createDNGVideos(videoCount, mContext.getUserId(), mUriList);
final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
// TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
@@ -227,27 +215,26 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
assertThat(itemCount).isEqualTo(videoCount);
// Select one item from Photo grid
- itemList.get(0).click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(0));
+ // Preview the item
UiObject item = itemList.get(1);
item.longClick();
mDevice.waitForIdle();
+ final UiObject addOrSelectButton = findPreviewAddOrSelectButton();
+ assertWithMessage("Timed out waiting for AddOrSelectButton to appear")
+ .that(addOrSelectButton.waitForExists(1000)).isTrue();
+
// Select the item from Preview
- final UiObject selectButton = findPreviewAddOrSelectButton();
- selectButton.click();
- mDevice.waitForIdle();
+ clickAndWait(addOrSelectButton);
mDevice.pressBack();
// Select one more item from Photo grid
- itemList.get(2).click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(2));
- final UiObject addButton = findAddButton();
- addButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findAddButton());
// Verify that all 3 items are returned
final ClipData clipData = mActivity.getResult().data.getClipData();
@@ -273,31 +260,21 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
final int itemCount = itemList.size();
assertThat(itemCount).isEqualTo(imageCount);
for (int i = 0; i < itemCount; i++) {
- final UiObject item = itemList.get(i);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(i));
}
- final UiObject viewSelectedButton = findViewSelectedButton();
- viewSelectedButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findViewSelectedButton());
// Swipe left three times
- swipeLeft();
- mDevice.waitForIdle();
- swipeLeft();
- mDevice.waitForIdle();
- swipeLeft();
- mDevice.waitForIdle();
+ swipeLeftAndWait();
+ swipeLeftAndWait();
+ swipeLeftAndWait();
// Deselect one item
- final UiObject selectCheckButton = findPreviewSelectCheckButton();
- selectCheckButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findPreviewSelectCheckButton());
- final UiObject addButton = findPreviewAddButton();
- addButton.click();
- mDevice.waitForIdle();
+ // Return selected items
+ clickAndWait(findPreviewAddButton());
final ClipData clipData = mActivity.getResult().data.getClipData();
final int count = clipData.getItemCount();
@@ -310,11 +287,145 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
}
@Test
+ public void testMultiSelect_PreviewVideoPlayPause() throws Exception {
+ launchPreviewMultipleWithVideos(/* videoCount */ 4);
+
+ // Check Play/Pause in first video
+ testVideoPreviewPlayPause();
+
+ // Move to second video
+ swipeLeftAndWait();
+ // Check Play/Pause in second video
+ testVideoPreviewPlayPause();
+
+ // Move to fourth video
+ swipeLeftAndWait();
+ swipeLeftAndWait();
+ // Check Play/Pause in fourth video
+ testVideoPreviewPlayPause();
+
+ final UiObject addButton = findPreviewAddButton();
+ addButton.click();
+ // We don't test the result of the picker here because the intention of the test is only to
+ // test the video controls
+ }
+
+ @Test
+ public void testMultiSelect_PreviewVideoMuteButton() throws Exception {
+ launchPreviewMultipleWithVideos(/* videoCount */ 4);
+
+ final UiObject playPauseButton = findPlayPauseButton();
+ final UiObject muteButton = findMuteButton();
+ final UiObject playerView = findPlayerView();
+
+ // set-up and wait for player controls to be sticky
+ setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton);
+
+ // Test 1: Initial state of the mute Button
+ // Check that initial state of mute button is `selected`, i.e., volume off
+ assertThat(muteButton.isSelected()).isTrue();
+
+ // Test 2: Click Mute Button
+ // Click to unmute the audio
+ clickAndWait(muteButton);
+ // Check that mute button state is `not selected`, i.e., it shows `volume up` icon
+ assertThat(muteButton.isSelected()).isFalse();
+ // Click on the muteButton and check that mute button status is now `selected`
+ clickAndWait(muteButton);
+ assertThat(muteButton.isSelected()).isTrue();
+
+ // Test 3: Swipe resumes mute state, with state of mute button = `not selected`
+ // Click muteButton again to check the next video resumes the previous video's mute state
+ clickAndWait(muteButton);
+ assertThat(muteButton.isSelected()).isFalse();
+ // check that next video resumed previous video's mute state
+ swipeLeftAndWait();
+ // set-up and wait for player controls to be sticky
+ setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton);
+ assertThat(muteButton.isSelected()).isFalse();
+
+ // Test 4: Swipe resumes mute state, with state of the button = `selected`
+ // Click muteButton again to check the next video resumes the previous video's mute state
+ clickAndWait(muteButton);
+ assertThat(muteButton.isSelected()).isTrue();
+ // Swipe to next page and check that muteButton is selected
+ swipeLeftAndWait();
+ // set-up and wait for player controls to be sticky
+ setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton);
+ assertThat(muteButton.isSelected()).isTrue();
+
+ // Test 5: Next preview resumes mute state
+ // Click muteButton again to check if next Preview launch resumes the muteButton state
+ clickAndWait(muteButton);
+ assertThat(muteButton.isSelected()).isFalse();
+ // Go back and launch preview again
+ mDevice.pressBack();
+ // set-up and wait for player controls to be sticky
+ clickAndWait(findViewSelectedButton());
+ setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton);
+ assertThat(muteButton.isSelected()).isFalse();
+
+ clickAndWait(findPreviewAddButton());
+ // We don't test the result of the picker here because the intention of the test is only to
+ // test the video controls
+ }
+
+ @Test
+ public void testMultiSelect_PreviewVideoControlsVisibility() throws Exception {
+ launchPreviewMultipleWithVideos(/* videoCount */ 3);
+
+ mDevice.waitForIdle();
+
+ final UiObject playPauseButton = findPlayPauseButton();
+ final UiObject muteButton = findMuteButton();
+ // Check that the player controls are visible
+ assertPlayerControlsVisible(playPauseButton, muteButton);
+
+ // Check that buttons auto hide.
+ assertPlayerControlsAutoHide(playPauseButton, muteButton);
+
+ final UiObject playerView = findPlayerView();
+ // Click on StyledPlayerView to make the video controls visible
+ clickAndWait(playerView);
+ assertPlayerControlsVisible(playPauseButton, muteButton);
+
+ // Wait for 1s and check that controls are still visible
+ assertPlayerControlsDontAutoHide(playPauseButton, muteButton);
+
+ // Click on StyledPlayerView and check that controls are no longer visible. Don't click in
+ // the center, clicking in the center may pause the video.
+ playerView.clickBottomRight();
+ mDevice.waitForIdle();
+ assertPlayerControlsHidden(playPauseButton, muteButton);
+
+ // Swipe left and check that controls are not visible
+ swipeLeftAndWait();
+ assertPlayerControlsHidden(playPauseButton, muteButton);
+
+ // Click on the StyledPlayerView and check that controls appear
+ clickAndWait(playerView);
+ assertPlayerControlsVisible(playPauseButton, muteButton);
+
+ // Swipe left to check that controls are now visible on swipe
+ swipeLeftAndWait();
+ assertPlayerControlsVisible(playPauseButton, muteButton);
+
+ // Check that the player controls are auto hidden in 1s
+ assertPlayerControlsAutoHide(playPauseButton, muteButton);
+
+ final UiObject addButton = findPreviewAddButton();
+ addButton.click();
+ // We don't test the result of the picker here because the intention of the test is only to
+ // test the video controls
+ }
+
+ @Test
public void testMimeTypeFilter() throws Exception {
final int videoCount = 2;
- createVideos(videoCount, mContext.getUserId(), mUriList);
+ createDNGVideos(videoCount, mContext.getUserId(), mUriList);
final int imageCount = 1;
createImages(imageCount, mContext.getUserId(), mUriList);
+
final String mimeType = "video/dng";
final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
@@ -328,14 +439,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
final int itemCount = itemList.size();
assertThat(itemCount).isAtLeast(videoCount);
for (int i = 0; i < itemCount; i++) {
- final UiObject item = itemList.get(i);
- item.click();
- mDevice.waitForIdle();
+ clickAndWait(itemList.get(i));
}
- final UiObject addButton = findAddButton();
- addButton.click();
- mDevice.waitForIdle();
+ clickAndWait(findAddButton());
final ClipData clipData = mActivity.getResult().data.getClipData();
final int count = clipData.getItemCount();
@@ -348,6 +455,89 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
}
}
+ private void testVideoPreviewPlayPause() throws Exception {
+ final UiObject playPauseButton = findPlayPauseButton();
+ final UiObject muteButton = findMuteButton();
+
+ // Wait for buttons to auto hide.
+ assertPlayerControlsAutoHide(playPauseButton, muteButton);
+
+ // Click on StyledPlayerView to make the video controls visible
+ clickAndWait(findPlayerView());
+
+ // PlayPause button is now pause button, click the button to pause the video.
+ clickAndWait(playPauseButton);
+
+ // Wait for 1s and check that play button is not auto hidden
+ assertPlayerControlsDontAutoHide(playPauseButton, muteButton);
+
+ // PlayPause button is now play button, click the button to play the video.
+ clickAndWait(playPauseButton);
+ // Check that pause button auto-hides in 1s.
+ assertPlayerControlsAutoHide(playPauseButton, muteButton);
+ }
+
+ private void launchPreviewMultipleWithVideos(int videoCount) throws Exception {
+ createVideos(videoCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
+ intent.setType("video/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(videoCount);
+ final int itemCount = itemList.size();
+
+ assertThat(itemCount).isEqualTo(videoCount);
+
+ for (int i = 0; i < itemCount; i++) {
+ clickAndWait(itemList.get(i));
+ }
+
+ clickAndWait(findViewSelectedButton());
+ }
+
+ private void setUpAndAssertStickyPlayerControls(UiObject playerView, UiObject playPauseButton,
+ UiObject muteButton) throws Exception {
+ // Check that buttons auto hide.
+ assertPlayerControlsAutoHide(playPauseButton, muteButton);
+ // Click on StyledPlayerView to make the video controls visible
+ clickAndWait(playerView);
+ assertPlayerControlsVisible(playPauseButton, muteButton);
+ }
+
+ private void assertPlayerControlsVisible(UiObject playPauseButton, UiObject muteButton) {
+ assertVisible(playPauseButton, "Expected play/pause button to be visible");
+ assertVisible(muteButton, "Expected mute button to be visible");
+ }
+
+ private void assertPlayerControlsHidden(UiObject playPauseButton, UiObject muteButton) {
+ assertHidden(playPauseButton, "Expected play/pause button to be hidden");
+ assertHidden(muteButton, "Expected mute button to be hidden");
+ }
+
+ private void assertPlayerControlsAutoHide(UiObject playPauseButton, UiObject muteButton) {
+ // These buttons should auto hide in 1 second after the video playback start. Since we can't
+ // identify the video playback start time, we wait for 2 seconds instead.
+ assertWithMessage("Expected play/pause button to auto hide in 2s")
+ .that(playPauseButton.waitUntilGone(2000)).isTrue();
+ assertHidden(muteButton, "Expected mute button to hide after 2s");
+ }
+
+ private void assertPlayerControlsDontAutoHide(UiObject playPauseButton, UiObject muteButton) {
+ assertWithMessage("Expected play/pause button to not auto hide in 1s")
+ .that(playPauseButton.waitUntilGone(1100)).isFalse();
+ assertVisible(muteButton, "Expected mute button to be still visible after 1s");
+ }
+
+ private void assertVisible(UiObject button, String message) {
+ assertWithMessage(message).that(button.exists()).isTrue();
+ }
+
+ private void assertHidden(UiObject button, String message) {
+ assertWithMessage(message).that(button.exists()).isFalse();
+ }
+
private static UiObject findViewSelectedButton() {
return new UiObject(new UiSelector().resourceIdMatches(
REGEX_PACKAGE_NAME + ":id/button_view_selected"));
@@ -358,9 +548,31 @@ public class PhotoPickerTest extends PhotoPickerBaseTest {
REGEX_PACKAGE_NAME + ":id/preview_select_check_button"));
}
- private void swipeLeft() {
+
+ private static UiObject findPlayerView() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/preview_player_view"));
+ }
+
+ private static UiObject findMuteButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/preview_mute"));
+ }
+
+ private static UiObject findPlayPauseButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/exo_play_pause"));
+ }
+
+ private void clickAndWait(UiObject uiObject) throws Exception {
+ uiObject.click();
+ mDevice.waitForIdle();
+ }
+
+ private void swipeLeftAndWait() {
final int width = mDevice.getDisplayWidth();
final int height = mDevice.getDisplayHeight();
- mDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10);
+ mDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 20);
+ mDevice.waitForIdle();
}
}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
index e0385ca89c9..d9ff84bed99 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
@@ -17,9 +17,7 @@
package android.photopicker.cts.util;
import static android.os.SystemProperties.getBoolean;
-import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
-import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
-import static android.provider.MediaStore.Files.FileColumns.MIME_TYPE;
+import static android.provider.MediaStore.Files.FileColumns;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -65,16 +63,22 @@ public class PhotoPickerAssertionsUtils {
public static void assertRedactedReadOnlyAccess(Uri uri) throws Exception {
assertThat(uri).isNotNull();
// TODO(b/205291616): Replace FileColumns.MIME_TYPE with PickerMediaColumns.MIME_TYPE
- final String[] projection = new String[]{ MIME_TYPE };
+ final String[] projection = new String[]{ FileColumns.MIME_TYPE };
final Context context = InstrumentationRegistry.getTargetContext();
final ContentResolver resolver = context.getContentResolver();
- final Cursor c = resolver.query(uri, projection, null, null);
- assertThat(c).isNotNull();
- assertThat(c.moveToFirst()).isTrue();
+ try (Cursor c = resolver.query(uri, projection, null, null)) {
+ assertThat(c).isNotNull();
+ assertThat(c.moveToFirst()).isTrue();
+
+ final String mimeType;
+ if (getBoolean("sys.photopicker.pickerdb.enabled", true)) {
+ // TODO(b/205291616): Replace FileColumns.MIME_TYPE with
+ // PickerMediaColumns.MIME_TYPE
+ mimeType = c.getString(c.getColumnIndex(FileColumns.MIME_TYPE));
+ } else {
+ mimeType = c.getString(c.getColumnIndex(FileColumns.MIME_TYPE));
+ }
- if (getBoolean("sys.photopicker.pickerdb.enabled", true)) {
- // TODO(b/205291616): Replace FileColumns.MIME_TYPE with PickerMediaColumns.MIME_TYPE
- final String mimeType = c.getString(c.getColumnIndex(MIME_TYPE));
if (mimeType.startsWith("image")) {
assertImageRedactedReadOnlyAccess(uri, resolver);
} else if (mimeType.startsWith("video")) {
@@ -82,24 +86,14 @@ public class PhotoPickerAssertionsUtils {
} else {
fail("The mime type is not as expected: " + mimeType);
}
- } else {
- final int mediaType = c.getInt(1);
- switch (mediaType) {
- case MEDIA_TYPE_IMAGE:
- assertImageRedactedReadOnlyAccess(uri, resolver);
- break;
- case MEDIA_TYPE_VIDEO:
- assertVideoRedactedReadOnlyAccess(uri, resolver);
- break;
- default:
- fail("The media type is not as expected: " + mediaType);
- }
}
}
private static void assertVideoRedactedReadOnlyAccess(Uri uri, ContentResolver resolver)
throws Exception {
// The location is redacted
+ // TODO(b/201505595): Make this method work for test_video.mp4. Currently it works only for
+ // test_video_dng.mp4
try (InputStream in = resolver.openInputStream(uri);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
FileUtils.copy(in, out);
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java
index 37a44f99915..8103b70f6cd 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java
@@ -49,6 +49,15 @@ public class PhotoPickerFilesUtils {
}
}
+ public static void createDNGVideos(int count, int userId, List<Uri> uriList)
+ throws Exception {
+ for (int i = 0; i < count; i++) {
+ final Uri uri = createDNGVideo(userId);
+ uriList.add(uri);
+ clearMediaOwner(uri, userId);
+ }
+ }
+
public static void createVideos(int count, int userId, List<Uri> uriList)
throws Exception {
for (int i = 0; i < count; i++) {
@@ -69,8 +78,14 @@ public class PhotoPickerFilesUtils {
ShellUtils.runShellCommand(cmd);
}
+ private static Uri createDNGVideo(int userId) throws Exception {
+ final Uri uri = stageMedia(R.raw.test_video_dng,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId);
+ return uri;
+ }
+
private static Uri createVideo(int userId) throws Exception {
- final Uri uri = stageMedia(R.raw.testvideo_meta,
+ final Uri uri = stageMedia(R.raw.test_video,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId);
return uri;
}
diff --git a/tests/providerui/AndroidManifest.xml b/tests/providerui/AndroidManifest.xml
index 2f1f791d9d8..a14df70d71b 100644
--- a/tests/providerui/AndroidManifest.xml
+++ b/tests/providerui/AndroidManifest.xml
@@ -23,9 +23,7 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!--
@@ -42,7 +40,7 @@
</intent>
</queries>
- <application android:requestLegacyExternalStorage = "true">
+ <application>
<uses-library android:name="android.test.runner"/>
<activity android:name="android.providerui.cts.GetResultActivity" />
diff --git a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
index 4342810763c..47fe16104d4 100644
--- a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
+++ b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java
@@ -26,12 +26,12 @@ import android.app.Activity;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -39,7 +39,6 @@ import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
-import android.os.UserManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.cts.ProviderTestUtils;
@@ -50,12 +49,12 @@ import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.system.Os;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
@@ -68,6 +67,7 @@ import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -171,7 +171,7 @@ public class MediaStoreUiTest {
}
@Test
- public void testGetDocumentUri_ThrowsWithoutPermission() throws Exception {
+ public void testGetDocumentUri_throwsWithoutPermission() throws Exception {
if (!supportsHardware()) return;
prepareFile();
@@ -185,7 +185,7 @@ public class MediaStoreUiTest {
}
@Test
- public void testGetDocumentUri_Symmetry_ExternalStorageProvider() throws Exception {
+ public void testGetDocumentUri_symmetry_externalStorageProvider() throws Exception {
if (!supportsHardware()) return;
prepareFile();
@@ -207,10 +207,10 @@ public class MediaStoreUiTest {
}
@Test
- public void testGetMediaUriAccess_MediaDocumentsProvider() throws Exception {
+ public void testGetMediaUriAccess_mediaDocumentsProvider() throws Exception {
if (!supportsHardware()) return;
- prepareFile();
+ prepareFile("TEST");
clearDocumentsUi();
final Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
@@ -228,6 +228,104 @@ public class MediaStoreUiTest {
assertAccessToMediaUri(mediaUri, mFile);
}
+ @Test
+ public void testOpenFile_onMediaDocumentsProvider_success() throws Exception {
+ if (!supportsHardware()) return;
+
+ final String rawText = "TEST";
+ // Stage a text file which contains raw text "TEST"
+ prepareFile(rawText);
+ clearDocumentsUi();
+ final Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mDevice.waitForIdle();
+
+ findDocument(mFile.getName()).click();
+ final Result result = mActivity.getResult();
+ final Uri uri = result.data.getData();
+ assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY, uri.getAuthority());
+
+ // Test reading
+ final byte[] expected = rawText.getBytes();
+ final byte[] actual = new byte[4];
+ try (ParcelFileDescriptor fd = mContext.getContentResolver()
+ .openFileDescriptor(uri, "r")) {
+ Os.read(fd.getFileDescriptor(), actual, 0, actual.length);
+ assertArrayEquals(expected, actual);
+ }
+
+ // Test write and read after it
+ final byte[] writtenText = "Hello World".getBytes();
+ final byte[] readText = new byte[11];
+ try (ParcelFileDescriptor fd = mContext.getContentResolver()
+ .openFileDescriptor(uri, "wt")) {
+ Os.write(fd.getFileDescriptor(), writtenText, 0, writtenText.length);
+ }
+ try (ParcelFileDescriptor fd = mContext.getContentResolver()
+ .openFileDescriptor(uri, "r")) {
+ Os.read(fd.getFileDescriptor(), readText, 0, readText.length);
+ assertArrayEquals(writtenText, readText);
+ }
+ }
+
+ @Test
+ public void testOpenFile_onMediaDocumentsProvider_failsWithoutAccess() throws Exception {
+ if (!supportsHardware()) return;
+
+ clearDocumentsUi();
+ final Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mDevice.waitForIdle();
+
+ String rawText = "TEST";
+ // Read and write grants will be provided to the file associated with this pair.
+ // Stages a text file which contains raw text "TEST"
+ Pair<Uri, File> uriFilePairWithGrants = prepareFileAndFetchDetails(rawText);
+ // Read and write grants will not be provided to the file associated with this pair
+ // Stages a text file which contains raw text "TEST"
+ Pair<Uri, File> uriFilePairWithoutGrants = prepareFileAndFetchDetails(rawText);
+ // Get access grants
+ findDocument(uriFilePairWithGrants.second.getName()).click();
+ final Result result = mActivity.getResult();
+ final Uri docUriOfFileWithAccess = result.data.getData();
+ // Creating doc URI for file by string replacement
+ Uri docUriOfFileWithoutAccess = Uri.parse(docUriOfFileWithAccess.toSafeString().replaceAll(
+ String.valueOf(ContentUris.parseId(uriFilePairWithGrants.first)),
+ String.valueOf(ContentUris.parseId(uriFilePairWithoutGrants.first))));
+
+ try {
+ assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY, docUriOfFileWithAccess.getAuthority());
+ assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY,
+ docUriOfFileWithoutAccess.getAuthority());
+ // Test reading
+ try (ParcelFileDescriptor fd = mContext.getContentResolver().openFileDescriptor(
+ docUriOfFileWithoutAccess, "r")) {
+ fail("Expecting security exception as file does not have read grants which "
+ + "are provided through ACTION_OPEN_DOCUMENT intent.");
+ } catch (SecurityException expected) {
+ // Expected security exception as file does not have read grants
+ }
+ // Test writing
+ try (ParcelFileDescriptor fd = mContext.getContentResolver().openFileDescriptor(
+ docUriOfFileWithoutAccess, "wt")) {
+ fail("Expecting security exception as file does not have write grants which "
+ + "are provided through ACTION_OPEN_DOCUMENT intent.");
+ } catch (SecurityException expected) {
+ // Expected security exception as file does not have write grants
+ }
+ } finally {
+ // Deleting files
+ uriFilePairWithGrants.second.delete();
+ uriFilePairWithoutGrants.second.delete();
+ }
+ }
+
private void assertAccessToMediaUri(Uri mediaUri, File file) {
final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
try (Cursor c = mContext.getContentResolver().query(
@@ -320,6 +418,28 @@ public class MediaStoreUiTest {
Log.v(TAG, "Staged " + mFile + " as " + mMediaStoreUri);
}
+ private void prepareFile(String rawText) throws Exception {
+ final File dir = new File(getVolumePath(mVolumeName),
+ Environment.DIRECTORY_DOCUMENTS);
+ final File file = new File(dir, "cts" + System.nanoTime() + ".txt");
+
+ mFile = stageFileWithRawText(rawText, file);
+ mMediaStoreUri = MediaStore.scanFile(mContext.getContentResolver(), mFile);
+
+ Log.v(TAG, "Staged " + mFile + " as " + mMediaStoreUri);
+ }
+
+ private Pair<Uri, File> prepareFileAndFetchDetails(String rawText) throws Exception {
+ final File dir = new File(getVolumePath(mVolumeName), Environment.DIRECTORY_DOCUMENTS);
+ final File file = new File(dir, "cts" + System.nanoTime() + ".txt");
+
+ File stagedFile = stageFileWithRawText(rawText, file);
+
+ Uri uri = MediaStore.scanFile(mContext.getContentResolver(), stagedFile);
+ Log.v(TAG, "Staged " + stagedFile + " as " + uri);
+ return Pair.create(uri, stagedFile);
+ }
+
private void assertToolbarTitleEquals(String targetPackageName, String label)
throws UiObjectNotFoundException {
final UiSelector toolbarUiSelector = new UiSelector().resourceId(
@@ -427,32 +547,28 @@ public class MediaStoreUiTest {
// The caller may be trying to stage into a location only available to
// the shell user, so we need to perform the entire copy as the shell
final Context context = InstrumentationRegistry.getTargetContext();
- UserManager userManager = context.getSystemService(UserManager.class);
- if (userManager.isSystemUser() &&
- FileUtils.contains(Environment.getStorageDirectory(), file)) {
- executeShellCommand("mkdir -p " + file.getParent());
-
- try (AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId)) {
- final File source = ParcelFileDescriptor.getFile(afd.getFileDescriptor());
- final long skip = afd.getStartOffset();
- final long count = afd.getLength();
-
- executeShellCommand(String.format("dd bs=1 if=%s skip=%d count=%d of=%s",
- source.getAbsolutePath(), skip, count, file.getAbsolutePath()));
+ final File dir = file.getParentFile();
+ dir.mkdirs();
+ if (!dir.exists()) {
+ throw new FileNotFoundException("Failed to create parent for " + file);
+ }
+ try (InputStream source = context.getResources().openRawResource(resId);
+ OutputStream target = new FileOutputStream(file)) {
+ FileUtils.copy(source, target);
+ }
+ return file;
+ }
- // Force sync to try updating other views
- executeShellCommand("sync");
- }
- } else {
- final File dir = file.getParentFile();
- dir.mkdirs();
- if (!dir.exists()) {
- throw new FileNotFoundException("Failed to create parent for " + file);
- }
- try (InputStream source = context.getResources().openRawResource(resId);
- OutputStream target = new FileOutputStream(file)) {
- FileUtils.copy(source, target);
- }
+ static File stageFileWithRawText(String rawText, File file) throws IOException {
+ final File dir = file.getParentFile();
+ dir.mkdirs();
+ if (!dir.exists()) {
+ throw new FileNotFoundException("Failed to create parent for " + file);
+ }
+ try (InputStream source = new ByteArrayInputStream(
+ rawText.getBytes(StandardCharsets.UTF_8));
+ OutputStream target = new FileOutputStream(file)) {
+ FileUtils.copy(source, target);
}
return file;
}
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index 4fd9b5152d3..e249fe342da 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -47,6 +47,7 @@ android_test {
":CtsUsePermissionApp30",
":CtsUsePermissionApp30WithBackground",
":CtsUsePermissionApp30WithBluetooth",
+ ":CtsUsePermissionApp31",
":CtsUsePermissionAppLatest",
":CtsUsePermissionAppLatestNone",
":CtsUsePermissionAppWithOverlay",
diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml
index 1536d580229..2342b6a4530 100644
--- a/tests/tests/permission3/AndroidTest.xml
+++ b/tests/tests/permission3/AndroidTest.xml
@@ -54,6 +54,7 @@
<option name="push" value="CtsUsePermissionApp30.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30.apk" />
<option name="push" value="CtsUsePermissionApp30WithBackground.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30WithBackground.apk" />
<option name="push" value="CtsUsePermissionApp30WithBluetooth.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30WithBluetooth.apk" />
+ <option name="push" value="CtsUsePermissionApp31.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp31.apk" />
<option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatest.apk" />
<option name="push" value="CtsUsePermissionAppLatestNone.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatestNone.apk" />
<option name="push" value="CtsUsePermissionAppWithOverlay.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppWithOverlay.apk" />
diff --git a/tests/tests/permission3/UsePermissionApp31/Android.bp b/tests/tests/permission3/UsePermissionApp31/Android.bp
new file mode 100644
index 00000000000..48a2d4fa901
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionApp31/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionApp31",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
+ target_sdk_version: "31",
+ min_sdk_version: "31",
+}
diff --git a/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml b/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml
new file mode 100644
index 00000000000..f6d9b29221b
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.permission3.cts.usepermission">
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index ece58d93bee..6c69a6522ac 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -52,6 +52,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsUsePermissionApp28.apk"
const val APP_APK_PATH_29 = "$APK_DIRECTORY/CtsUsePermissionApp29.apk"
const val APP_APK_PATH_30 = "$APK_DIRECTORY/CtsUsePermissionApp30.apk"
+ const val APP_APK_PATH_31 = "$APK_DIRECTORY/CtsUsePermissionApp31.apk"
+
const val APP_APK_PATH_30_WITH_BACKGROUND =
"$APK_DIRECTORY/CtsUsePermissionApp30WithBackground.apk"
const val APP_APK_PATH_30_WITH_BLUETOOTH =
diff --git a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
new file mode 100644
index 00000000000..37e9d41fe5c
--- /dev/null
+++ b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 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.permission3.cts
+
+import android.content.Intent
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
+import android.location.LocationManager
+import android.os.Build
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.support.test.uiautomator.By
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Banner card display tests on sensors being blocked
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+class SensorBlockedBannerTest : BaseUsePermissionTest() {
+ companion object {
+ const val LOCATION = -1
+ const val WARNING_BANNER_ENABLED = "warning_banner_enabled"
+ }
+
+ val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
+ val locationManager = context.getSystemService(LocationManager::class.java)!!
+ private val originalEnabledValue = callWithShellPermissionIdentity {
+ DeviceConfig.getString(DeviceConfig.NAMESPACE_PRIVACY,
+ WARNING_BANNER_ENABLED, false.toString())
+ }
+
+ private val permToLabel = mapOf(CAMERA to "privdash_label_camera",
+ MICROPHONE to "privdash_label_microphone",
+ LOCATION to "privdash_label_location")
+
+ private val permToTitle = mapOf(CAMERA to "blocked_camera_title",
+ MICROPHONE to "blocked_microphone_title",
+ LOCATION to "blocked_location_title")
+
+ @Before
+ fun setup() {
+ Assume.assumeFalse(isTv)
+ // TODO(b/203784852) Auto will eventually support the blocked sensor banner, but there won't
+ // be support in T or below
+ Assume.assumeFalse(isAutomotive)
+ installPackage(APP_APK_PATH_31)
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ WARNING_BANNER_ENABLED, true.toString(), false)
+ }
+ }
+
+ @After
+ fun restoreWarningBannerState() {
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+ WARNING_BANNER_ENABLED, originalEnabledValue, false)
+ }
+ }
+
+ private fun navigateAndTest(sensor: Int) {
+ val permLabel = permToLabel.getOrDefault(sensor, "Break")
+ val intent = Intent(Settings.ACTION_PRIVACY_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ click(By.text(getPermissionControllerString("app_permission_manager")))
+ click(By.text(getPermissionControllerString(permLabel)))
+ val bannerTitle = permToTitle.getOrDefault(sensor, "Break")
+ waitFindObject(By.text(getPermissionControllerString(bannerTitle)))
+ pressBack()
+ pressBack()
+ pressBack()
+ }
+
+ private fun runSensorTest(sensor: Int) {
+ val blocked = isSensorPrivacyEnabled(sensor)
+ if (!blocked) {
+ setSensor(sensor, true)
+ }
+ navigateAndTest(sensor)
+ if (!blocked) {
+ setSensor(sensor, false)
+ }
+ }
+
+ @Test
+ fun testCameraCardDisplayed() {
+ Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(CAMERA))
+ runSensorTest(CAMERA)
+ }
+
+ @Test
+ fun testMicCardDisplayed() {
+ Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(MICROPHONE))
+ runSensorTest(MICROPHONE)
+ }
+
+ @Test
+ fun testLocationCardDisplayed() {
+ runSensorTest(LOCATION)
+ }
+
+ private fun setSensor(sensor: Int, enable: Boolean) {
+ if (sensor == LOCATION) {
+ runWithShellPermissionIdentity {
+ locationManager.setLocationEnabledForUser(!enable,
+ android.os.Process.myUserHandle())
+ if (enable) {
+ try {
+ waitFindObjectOrNull(By.text("CLOSE"))?.click()
+ } catch (e: Exception) {
+ // Do nothing, warning didn't show up so test can proceed
+ }
+ }
+ }
+ } else {
+ runWithShellPermissionIdentity {
+ sensorPrivacyManager.setSensorPrivacy(SensorPrivacyManager.Sources.OTHER,
+ sensor, enable)
+ }
+ }
+ }
+
+ private fun isSensorPrivacyEnabled(sensor: Int): Boolean {
+ return if (sensor == LOCATION) {
+ callWithShellPermissionIdentity {
+ !locationManager.isLocationEnabled()
+ }
+ } else {
+ callWithShellPermissionIdentity {
+ sensorPrivacyManager.isSensorPrivacyEnabled(sensor)
+ }
+ }
+ }
+}