Merge "cts: unregister softap callback after finish the test" into rvc-dev
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index aa28a97..af9d4d2 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -17,21 +17,22 @@
package android.appsecurity.cts;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.LargeTest;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.util.AbiFormatter;
-import com.android.tradefed.util.AbiUtils;
/**
* Tests that verify intent filters.
*/
+@LargeTest
@AppModeFull(reason="Instant applications can never be system or privileged")
public class PrivilegedUpdateTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
private static final String TAG = "PrivilegedUpdateTests";
@@ -151,6 +152,28 @@
}
}
+ public void testUpdatedSystemAppPreservedOnReboot() throws Exception {
+ if (!isDefaultAbi()) {
+ Log.w(TAG, "Skipping test for non-default abi.");
+ return;
+ }
+
+ getDevice().executeShellCommand("pm enable " + SHIM_PKG);
+ runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testPrivAppAndEnabled");
+ try {
+ assertNull(getDevice().installPackage(
+ mBuildHelper.getTestFile(SHIM_UPDATE_APK), true));
+ getDevice().executeShellCommand("pm enable " + SHIM_PKG);
+ runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testUpdatedPrivAppAndEnabled");
+
+ getDevice().reboot();
+
+ runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testUpdatedPrivAppAndEnabled");
+ } finally {
+ getDevice().uninstallPackage(SHIM_PKG);
+ }
+ }
+
private void runDeviceTests(String packageName, String testClassName, String testMethodName)
throws DeviceNotAvailableException {
Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
index 95169b6..a1487af 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.privilegedupdate">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
<application android:label="PrivilegedUpdateApp">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity" />
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
index 606ada1..18962a9 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
@@ -35,29 +35,27 @@
private static final String PRIVILEGED_SHIM_PKG = "com.android.cts.priv.ctsshim";
public void testPrivAppAndEnabled() throws Exception {
- assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
- 0);
+ assertEquals(0, (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
}
public void testPrivAppAndDisabled() throws Exception {
- assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
- 0);
+ assertEquals(0, (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
}
public void testUpdatedPrivAppAndEnabled() throws Exception {
- assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ assertEquals(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
}
public void testUpdatedPrivAppAndDisabled() throws Exception {
- assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ assertEquals(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
}
diff --git a/tests/JobScheduler/Android.bp b/tests/JobScheduler/Android.bp
index 9869c74..e9be178 100644
--- a/tests/JobScheduler/Android.bp
+++ b/tests/JobScheduler/Android.bp
@@ -19,6 +19,7 @@
"compatibility-device-util-axt",
"ub-uiautomator",
"androidx.test.rules",
+ "cts-wm-util",
],
libs: ["android.test.base.stubs"],
srcs: [
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index effabb7..895c563 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -232,7 +232,7 @@
sendScheduleJobBroadcast(false);
assertFalse("Job started for restricted app",
mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
- mTestAppInterface.startAndKeepTestActivity();
+ mTestAppInterface.startAndKeepTestActivity(true);
assertTrue("Job did not start when app had an activity",
mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
index 36d1825..d871a2b 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
@@ -18,6 +18,7 @@
import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED;
import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import android.app.job.JobParameters;
import android.content.BroadcastReceiver;
@@ -28,6 +29,7 @@
import android.jobscheduler.cts.jobtestapp.TestActivity;
import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
import android.os.SystemClock;
+import android.server.wm.WindowManagerStateHelper;
import android.util.Log;
/**
@@ -77,10 +79,18 @@
}
void startAndKeepTestActivity() {
+ startAndKeepTestActivity(false);
+ }
+
+ void startAndKeepTestActivity(boolean waitForResume) {
final Intent testActivity = new Intent();
testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- testActivity.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+ ComponentName testComponentName = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+ testActivity.setComponent(testComponentName);
mContext.startActivity(testActivity);
+ if (waitForResume) {
+ new WindowManagerStateHelper().waitForActivityState(testComponentName, STATE_RESUMED);
+ }
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 4de36b9..10934dd 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -474,8 +474,7 @@
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
if (data == null) {
- data = new Notification.BubbleMetadata.Builder()
- .createIntentBubble(pendingIntent,
+ data = new Notification.BubbleMetadata.Builder(pendingIntent,
Icon.createWithResource(mContext, R.drawable.black))
.build();
}
@@ -3077,9 +3076,8 @@
.setSmallIcon(android.R.drawable.sym_def_app_icon);
// BubbleMetadata with manifest shortcut
- Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID_MANIFEST)
- .build();
+ Notification.BubbleMetadata data =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_MANIFEST).build();
sendAndVerifyBubble(1, nb, data, false /* shouldBeBubble */);
}
@@ -3134,9 +3132,9 @@
.setSmallIcon(android.R.drawable.sym_def_app_icon);
// BubbleMetadata with our dynamic shortcut ic
- Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID_DYNAMIC)
- .build();
+ Notification.BubbleMetadata data =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_DYNAMIC)
+ .build();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, data, shouldBeBubble);
@@ -3181,9 +3179,9 @@
.setSmallIcon(android.R.drawable.sym_def_app_icon);
// BubbleMetadata with shortcut that doesn't exist
- Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble("shortcutDoesntExist")
- .build();
+ Notification.BubbleMetadata data =
+ new Notification.BubbleMetadata.Builder("shortcutDoesntExist")
+ .build();
sendAndVerifyBubble(1, nb, data, false);
}
@@ -3198,9 +3196,9 @@
toggleBubbleSetting(true);
// BubbleMetadata with manifest shortcut
- Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID_MANIFEST)
- .build();
+ Notification.BubbleMetadata data =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_MANIFEST)
+ .build();
sendAndVerifyBubble(1, null /* use default notif builder */, data,
false /* shouldBeBubble */);
@@ -3257,10 +3255,10 @@
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
- // BubbleMetadata with our dynamic shortcut ic
- Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID_DYNAMIC)
- .build();
+ // BubbleMetadata with our dynamic shortcut
+ Notification.BubbleMetadata data =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_DYNAMIC)
+ .build();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, data, shouldBeBubble);
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index 439f3a9..b9f8784 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -625,15 +625,14 @@
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
.setDesiredHeight(BUBBLE_HEIGHT)
- .createIntentBubble(bubbleIntent, icon)
.setDeleteIntent(deleteIntent);
Notification.BubbleMetadata data = metadataBuilder.build();
assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight());
- assertEquals(icon, data.getBubbleIcon());
- assertEquals(bubbleIntent, data.getBubbleIntent());
+ assertEquals(icon, data.getIcon());
+ assertEquals(bubbleIntent, data.getIntent());
assertEquals(deleteIntent, data.getDeleteIntent());
assertFalse(data.isNotificationSuppressed());
assertFalse(data.getAutoExpandBubble());
@@ -644,18 +643,17 @@
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
.setDesiredHeight(BUBBLE_HEIGHT)
.setAutoExpandBubble(true)
.setSuppressNotification(true)
- .createIntentBubble(bubbleIntent, icon)
.setDeleteIntent(deleteIntent)
.build();
writeAndReadParcelable(metadata);
assertEquals(BUBBLE_HEIGHT, metadata.getDesiredHeight());
- assertEquals(icon, metadata.getBubbleIcon());
- assertEquals(bubbleIntent, metadata.getBubbleIntent());
+ assertEquals(icon, metadata.getIcon());
+ assertEquals(bubbleIntent, metadata.getIntent());
assertEquals(deleteIntent, metadata.getDeleteIntent());
assertTrue(metadata.getAutoExpandBubble());
assertTrue(metadata.isNotificationSuppressed());
@@ -664,9 +662,8 @@
public void testBubbleMetadataBuilder_shortcutId() {
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
.setDesiredHeight(BUBBLE_HEIGHT)
- .createShortcutBubble(BUBBLE_SHORTCUT_ID)
.setDeleteIntent(deleteIntent);
Notification.BubbleMetadata data = metadataBuilder.build();
@@ -681,11 +678,10 @@
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
.setDesiredHeight(BUBBLE_HEIGHT)
.setAutoExpandBubble(true)
.setSuppressNotification(true)
- .createShortcutBubble(BUBBLE_SHORTCUT_ID)
.setDeleteIntent(deleteIntent)
.build();
@@ -701,73 +697,67 @@
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
.setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
- .createIntentBubble(bubbleIntent, icon)
.build();
writeAndReadParcelable(metadata);
assertEquals(BUBBLE_HEIGHT_RESID, metadata.getDesiredHeightResId());
- assertEquals(icon, metadata.getBubbleIcon());
- assertEquals(bubbleIntent, metadata.getBubbleIntent());
+ assertEquals(icon, metadata.getIcon());
+ assertEquals(bubbleIntent, metadata.getIntent());
assertFalse(metadata.getAutoExpandBubble());
assertFalse(metadata.isNotificationSuppressed());
}
public void testBubbleMetadataBuilder_throwForNoIntentNoShortcut() {
- Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
- .setDesiredHeight(BUBBLE_HEIGHT);
+ new Notification.BubbleMetadata.Builder();
try {
metadataBuilder.build();
- fail("Should have thrown IllegalArgumentException, no pending intent or shortcutId");
- } catch (IllegalStateException e) {
+ fail("Should have thrown exception, no pending intent or shortcutId");
+ } catch (NullPointerException e) {
// expected
}
}
public void testBubbleMetadataBuilder_noThrowWithShortcut() {
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
- .setDesiredHeight(BUBBLE_HEIGHT)
- .createShortcutBubble(BUBBLE_SHORTCUT_ID);
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+ .setDesiredHeight(BUBBLE_HEIGHT);
Notification.BubbleMetadata metadata = metadataBuilder.build();
assertNotNull(metadata.getShortcutId());
- assertNull(metadata.getBubbleIcon());
- assertNull(metadata.getBubbleIntent());
+ assertNull(metadata.getIcon());
+ assertNull(metadata.getIntent());
}
- public void testBubbleMetadataBuilder_shortcutOverwritesIconIntent() {
+ public void testBubbleMetadataBuilder_shortcutBuilder_throwsForSetIntent() {
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- Icon icon = Icon.createWithResource(mContext, 1);
- Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder()
- .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
- .createIntentBubble(bubbleIntent, icon)
- .createShortcutBubble(BUBBLE_SHORTCUT_ID)
- .build();
- assertNotNull(metadata.getShortcutId());
- assertNull(metadata.getBubbleIcon());
- assertNull(metadata.getBubbleIntent());
+ try {
+ Notification.BubbleMetadata.Builder metadataBuilder =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+ .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
+ .setIntent(bubbleIntent);
+ fail("Should have thrown exception, can't set intent on shortcut builder");
+ } catch (Exception e) {
+ // expected
+ }
}
- public void testBubbleMetadataBuilder_intentIconOverwritesShortcut() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- Icon icon = Icon.createWithResource(mContext, 1);
- Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
- .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
- .createShortcutBubble(BUBBLE_SHORTCUT_ID)
- .createIntentBubble(bubbleIntent, icon);
- Notification.BubbleMetadata metadata = metadataBuilder.build();
- assertNull(metadata.getShortcutId());
- assertNotNull(metadata.getBubbleIcon());
- assertNotNull(metadata.getBubbleIntent());
+ public void testBubbleMetadataBuilder_shortcutBuilder_throwsForSetIcon() {
+ try {
+ Icon icon = Icon.createWithResource(mContext, 1);
+ Notification.BubbleMetadata.Builder metadataBuilder =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+ .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
+ .setIcon(icon);
+ fail("Should have thrown exception, can't set icon on shortcut builder");
+ } catch (Exception e) {
+ // expected
+ }
}
public void testBubbleMetadataBuilder_notifBubbleShortcutIds_match_noThrow() {
- Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID).build();
+ Notification.BubbleMetadata metadata =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID).build();
mNotification = new Notification.Builder(mContext, CHANNEL.getId())
.setSmallIcon(1)
@@ -781,8 +771,8 @@
}
public void testBubbleMetadataBuilder_notifBubbleShortcutIds_different_throw() {
- Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
- .createShortcutBubble(BUBBLE_SHORTCUT_ID).build();
+ Notification.BubbleMetadata metadata =
+ new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID).build();
Notification.Builder nb = new Notification.Builder(mContext, CHANNEL.getId())
.setSmallIcon(1)
@@ -806,11 +796,10 @@
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
- .createIntentBubble(bubbleIntent, icon);
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
Notification.BubbleMetadata metadata = metadataBuilder.build();
- assertNotNull(metadata.getBubbleIcon());
- assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getBubbleIcon().getType());
+ assertNotNull(metadata.getIcon());
+ assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getIcon().getType());
}
public void testBubbleMetadataBuilder_noThrowForNonBitmapIcon() {
@@ -818,11 +807,10 @@
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
- .createIntentBubble(bubbleIntent, icon);
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
Notification.BubbleMetadata metadata = metadataBuilder.build();
- assertNotNull(metadata.getBubbleIcon());
- assertEquals(TYPE_RESOURCE, metadata.getBubbleIcon().getType());
+ assertNotNull(metadata.getIcon());
+ assertEquals(TYPE_RESOURCE, metadata.getIcon().getType());
}
public void testBubbleMetadataBuilder_replaceHeightRes() {
@@ -830,10 +818,9 @@
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
.setDesiredHeight(BUBBLE_HEIGHT)
.setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
- .createIntentBubble(bubbleIntent, icon)
.setDeleteIntent(deleteIntent);
Notification.BubbleMetadata data = metadataBuilder.build();
@@ -848,10 +835,9 @@
PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
- new Notification.BubbleMetadata.Builder()
+ new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
.setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
.setDesiredHeight(BUBBLE_HEIGHT)
- .createIntentBubble(bubbleIntent, icon)
.setDeleteIntent(deleteIntent);
Notification.BubbleMetadata data = metadataBuilder.build();
@@ -923,9 +909,9 @@
private Notification.BubbleMetadata makeBubbleMetadata() {
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- return new Notification.BubbleMetadata.Builder()
- .createIntentBubble(bubbleIntent, Icon.createWithResource(mContext, 1))
- .setDesiredHeight(BUBBLE_HEIGHT)
- .build();
+ return new Notification.BubbleMetadata.Builder(bubbleIntent,
+ Icon.createWithResource(mContext, 1))
+ .setDesiredHeight(BUBBLE_HEIGHT)
+ .build();
}
}
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
index 85221d3..efe5700 100644
--- a/tests/camera/AndroidManifest.xml
+++ b/tests/camera/AndroidManifest.xml
@@ -70,6 +70,13 @@
android:process=":camera2ActivityProcess">
</activity>
+ <activity android:name="android.hardware.camera2.cts.Camera2OfflineTestActivity"
+ android:label="RemoteCamera2OfflineTestActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:process=":camera2ActivityProcess">
+ </activity>
+
<activity android:name="android.hardware.multiprocess.camera.cts.MediaRecorderCameraActivity"
android:label="RemoteMediaRecorderCameraActivity"
android:screenOrientation="landscape"
diff --git a/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java
new file mode 100644
index 0000000..2483eef
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.OFFLINE_CAMERA_ID;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.multiprocess.camera.cts.ErrorLoggingService;
+import android.hardware.multiprocess.camera.cts.TestConstants;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * Activity implementing basic access of the Camera2 API.
+ *
+ * <p />
+ * This will log all errors to {@link android.hardware.multiprocess.camera.cts.ErrorLoggingService}.
+ */
+public class Camera2OfflineTestActivity extends Activity {
+ private static final String TAG = "Camera2OfflineTestActivity";
+
+ ErrorLoggingService.ErrorServiceConnection mErrorServiceConnection;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate called.");
+ super.onCreate(savedInstanceState);
+ mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(this);
+ mErrorServiceConnection.start();
+ }
+
+ @Override
+ protected void onPause() {
+ Log.i(TAG, "onPause called.");
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ Log.i(TAG, "onResume called.");
+ super.onResume();
+
+ try {
+ CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
+
+ if (manager == null) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+ " could not connect camera service");
+ return;
+ }
+ Intent intent = getIntent();
+ Bundle bundledExtras = intent.getExtras();
+ if (null == bundledExtras) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+ " not bundled intent extras");
+ return;
+ }
+
+ String cameraId = bundledExtras.getString(OFFLINE_CAMERA_ID);
+ if (null == cameraId) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+ " no camera id present in bundled extra");
+ return;
+ }
+
+ manager.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ super.onCameraAvailable(cameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_AVAILABLE,
+ cameraId);
+ Log.i(TAG, "Camera " + cameraId + " is available");
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ super.onCameraUnavailable(cameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_UNAVAILABLE,
+ cameraId);
+ Log.i(TAG, "Camera " + cameraId + " is unavailable");
+ }
+
+ @Override
+ public void onPhysicalCameraAvailable(String cameraId, String physicalCameraId) {
+ super.onPhysicalCameraAvailable(cameraId, physicalCameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_AVAILABLE,
+ cameraId + " : " + physicalCameraId);
+ Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is available");
+ }
+
+ @Override
+ public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+ super.onPhysicalCameraUnavailable(cameraId, physicalCameraId);
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_UNAVAILABLE,
+ cameraId + " : " + physicalCameraId);
+ Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is unavailable");
+ }
+ }, null);
+
+ manager.openCamera(cameraId, new CameraDevice.StateCallback() {
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_CONNECT,
+ cameraId);
+ Log.i(TAG, "Camera " + cameraId + " is opened");
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_EVICTED,
+ cameraId);
+ Log.i(TAG, "Camera " + cameraId + " is disconnected");
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int i) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+ " Camera " + cameraId + " experienced error " + i);
+ Log.e(TAG, "Camera " + cameraId + " onError called with error " + i);
+ }
+ }, null);
+ } catch (CameraAccessException e) {
+ mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+ " camera exception during connection: " + e);
+ Log.e(TAG, "Access exception: " + e);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.i(TAG, "onDestroy called.");
+ super.onDestroy();
+ if (mErrorServiceConnection != null) {
+ mErrorServiceConnection.stop();
+ mErrorServiceConnection = null;
+ }
+ }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
index a897944..e504803 100644
--- a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
@@ -24,8 +24,11 @@
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingOfflineSessionCallback;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.ImageFormat;
-import android.hardware.Camera;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
@@ -34,7 +37,13 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.multiprocess.camera.cts.ErrorLoggingService;
+import android.hardware.multiprocess.camera.cts.TestConstants;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -46,6 +55,7 @@
import org.junit.Test;
import java.lang.IllegalArgumentException;
+import java.util.concurrent.TimeoutException;
import java.util.ArrayList;
import java.util.List;
@@ -54,9 +64,30 @@
private static final String TAG = "OfflineSessionTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final String REMOTE_PROCESS_NAME = "camera2ActivityProcess";
+ private final java.lang.Class<?> REMOTE_PROCESS_CLASS = Camera2OfflineTestActivity.class;
+ private static final Size MANDATORY_STREAM_BOUND = new Size(1920, 1080);
private static final int WAIT_FOR_FRAMES_TIMEOUT_MS = 3000;
private static final int WAIT_FOR_STATE_TIMEOUT_MS = 5000;
+ private static final int WAIT_FOR_REMOTE_ACTIVITY_LAUNCH_MS = 2000;
+ private static final int WAIT_FOR_REMOTE_ACTIVITY_DESTROY_MS = 2000;
+
+ private enum OfflineTestSequence {
+ /** Regular offline switch without extra calls */
+ NoExtraSteps,
+
+ /** Offline switch followed by immediate offline session close */
+ CloseOfflineSession,
+
+ /** Offline switch followed in parallel with immediate device close and same device open
+ * in a separate activity
+ */
+ CloseDeviceAndOpenRemote,
+
+ /** Offline session running in parallel with reinitialized regular capture session */
+ InitializeRegularSession,
+ }
/**
* Test offline switch behavior in case of invalid/bad input.
@@ -171,8 +202,8 @@
}
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
- false /*closeDevice*/, false /*closeSession*/);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+ ImageFormat.JPEG, OfflineTestSequence.NoExtraSteps);
} finally {
closeDevice();
}
@@ -213,8 +244,8 @@
mCameraIdsUnderTest[i], mCameraManager, ImageFormat.DEPTH_JPEG,
null /*bound*/);
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(depthJpegSizes.get(0), ImageFormat.DEPTH_JPEG,
- false /*closeDevice*/, false /*closeSession*/);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], depthJpegSizes.get(0),
+ ImageFormat.DEPTH_JPEG, OfflineTestSequence.NoExtraSteps);
} finally {
closeDevice();
}
@@ -254,8 +285,8 @@
List<Size> heicSizes = CameraTestUtils.getSupportedHeicSizes(
mCameraIdsUnderTest[i], mCameraManager, null /*bound*/);
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(heicSizes.get(0), ImageFormat.HEIC,
- false /*closeDevice*/, false /*closeSession*/);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], heicSizes.get(0),
+ ImageFormat.HEIC, OfflineTestSequence.NoExtraSteps);
} finally {
closeDevice();
}
@@ -263,13 +294,17 @@
}
/**
- * Test camera offline session behavior during inactive camera device.
+ * Test camera offline session behavior after close and reopen.
*
- * <p> Verify that closing the initial camera device does not impact the
- * offline camera session.</p>
+ * <p> Verify that closing the initial camera device and opening the same
+ * sensor during offline processing does not have any unexpected side effects.</p>
*/
@Test
- public void testDeviceClose() throws Exception {
+ public void testDeviceCloseAndOpen() throws Exception {
+ ErrorLoggingService.ErrorServiceConnection errorConnection =
+ new ErrorLoggingService.ErrorServiceConnection(mContext);
+
+ errorConnection.start();
for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
@@ -287,12 +322,26 @@
}
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
- true /*closeDevice*/, false /*closeSession*/);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+ ImageFormat.JPEG, OfflineTestSequence.CloseDeviceAndOpenRemote);
+
+ // Verify that the remote camera was opened correctly
+ List<ErrorLoggingService.LogEvent> allEvents = null;
+ try {
+ allEvents = errorConnection.getLog(WAIT_FOR_STATE_TIMEOUT_MS,
+ TestConstants.EVENT_CAMERA_CONNECT);
+ } catch (TimeoutException e) {
+ fail("Timed out waiting on remote offline process error log!");
+ }
+ assertNotNull("Failed to connect to camera device in remote offline process!",
+ allEvents);
} finally {
closeDevice();
+
}
}
+
+ errorConnection.stop();
}
/**
@@ -320,8 +369,41 @@
}
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
- false /*closeDevice*/, true /*closeSession*/);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+ ImageFormat.JPEG, OfflineTestSequence.CloseOfflineSession);
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Test camera offline session in case of new capture session
+ *
+ * <p>Verify that clients are able to initialize a new regular capture session
+ * in parallel with the offline session.</p>
+ */
+ @Test
+ public void testOfflineSessionWithRegularSession() throws Exception {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ try {
+ Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
+
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+ " does not support color outputs, skipping");
+ continue;
+ }
+
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isOfflineProcessingSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+ " does not support offline processing, skipping");
+ continue;
+ }
+
+ openDevice(mCameraIdsUnderTest[i]);
+ camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+ ImageFormat.JPEG, OfflineTestSequence.InitializeRegularSession);
} finally {
closeDevice();
}
@@ -363,35 +445,38 @@
}
}
- // Find the last frame number received in results and failures.
- private long findLastFrameNumber(SimpleCaptureCallback captureListener) {
- long lastFrameNumber = -1;
- while (captureListener.hasMoreResults()) {
- TotalCaptureResult result = captureListener.getTotalCaptureResult(0 /*timeout*/);
- if (lastFrameNumber < result.getFrameNumber()) {
- lastFrameNumber = result.getFrameNumber();
+ private void verifyCaptureResults(SimpleCaptureCallback resultListener,
+ SimpleImageReaderListener imageListener, int sequenceId, boolean offlineResults)
+ throws Exception {
+ long sequenceLastFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+ sequenceId, 0 /*timeoutMs*/);
+
+ long lastFrameNumberReceived = -1;
+ while (resultListener.hasMoreResults()) {
+ TotalCaptureResult result = resultListener.getTotalCaptureResult(0 /*timeout*/);
+ if (lastFrameNumberReceived < result.getFrameNumber()) {
+ lastFrameNumberReceived = result.getFrameNumber();
+ }
+
+ if (imageListener != null) {
+ long resultTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+ Image offlineImage = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+ assertEquals("Offline image timestamp: " + offlineImage.getTimestamp() +
+ " doesn't match with the result timestamp: " + resultTimestamp,
+ offlineImage.getTimestamp(), resultTimestamp);
}
}
- while (captureListener.hasMoreFailures()) {
- ArrayList<CaptureFailure> failures = captureListener.getCaptureFailures(
+ while (resultListener.hasMoreFailures()) {
+ ArrayList<CaptureFailure> failures = resultListener.getCaptureFailures(
/*maxNumFailures*/ 1);
for (CaptureFailure failure : failures) {
- if (lastFrameNumber < failure.getFrameNumber()) {
- lastFrameNumber = failure.getFrameNumber();
+ if (lastFrameNumberReceived < failure.getFrameNumber()) {
+ lastFrameNumberReceived = failure.getFrameNumber();
}
}
}
- return lastFrameNumber;
- }
-
- private void verifyCaptureResults(SimpleCaptureCallback resultListener, int sequenceId,
- boolean offlineResults) {
- long sequenceLastFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
- sequenceId, 0 /*timeoutMs*/);
-
- long lastFrameNumberReceived = findLastFrameNumber(resultListener);
String assertString = offlineResults ?
"Last offline frame number from " +
"onCaptureSequenceCompleted (%d) doesn't match the last frame number " +
@@ -403,16 +488,34 @@
sequenceLastFrameNumber, lastFrameNumberReceived);
}
- private void camera2OfflineSessionTest(Size offlineSize, int offlineFormat, boolean closeDevice,
- boolean closeSession) throws Exception {
+ private void camera2OfflineSessionTest(String cameraId, Size offlineSize, int offlineFormat,
+ OfflineTestSequence testSequence) throws Exception {
+ int remoteOfflinePID = -1;
+ Size previewSize = mOrderedPreviewSizes.get(0);
+ for (Size sz : mOrderedPreviewSizes) {
+ if (sz.getWidth() <= MANDATORY_STREAM_BOUND.getWidth() && sz.getHeight() <=
+ MANDATORY_STREAM_BOUND.getHeight()) {
+ previewSize = sz;
+ break;
+ }
+ }
+ Size privateSize = previewSize;
+ if (mAllStaticInfo.get(cameraId).isPrivateReprocessingSupported()) {
+ privateSize = mAllStaticInfo.get(cameraId).getSortedSizesForInputFormat(
+ ImageFormat.PRIVATE, MANDATORY_STREAM_BOUND).get(0);
+ }
+
CaptureRequest.Builder previewRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
CaptureRequest.Builder stillCaptureRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
- Size previewSize = mOrderedPreviewSizes.get(0);
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ SimpleCaptureCallback regularResultListener = new SimpleCaptureCallback();
SimpleCaptureCallback offlineResultListener = new SimpleCaptureCallback();
SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+ ImageReader privateReader = null;
+ ImageReader yuvCallbackReader = null;
+ ImageReader jpegReader = null;
// Update preview size.
updatePreviewSurface(previewSize);
@@ -441,18 +544,7 @@
// Start preview.
int repeatingSeqId = mSession.setRepeatingRequest(previewRequest.build(), resultListener,
mHandler);
-
- CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
-
- Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
- assertNotNull("Can't read a capture result timestamp", timestamp);
-
- CaptureResult result2 = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
-
- Long timestamp2 = result2.get(CaptureResult.SENSOR_TIMESTAMP);
- assertNotNull("Can't read a capture result 2 timestamp", timestamp2);
-
- assertTrue("Bad timestamps", timestamp2 > timestamp);
+ checkInitialResults(resultListener);
ArrayList<Integer> allowedOfflineStates = new ArrayList<Integer>();
allowedOfflineStates.add(BlockingOfflineSessionCallback.STATE_READY);
@@ -487,45 +579,128 @@
verify(mockOfflineCb, times(0)).onError(offlineSession,
CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
- verifyCaptureResults(resultListener, repeatingSeqId, false /*offlineResults*/);
- verifyCaptureResults(offlineResultListener, offlineSeqId, true /*offlineResults*/);
+ verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
+ false /*offlineResults*/);
+ verifyCaptureResults(offlineResultListener, null /*imageListener*/, offlineSeqId,
+ true /*offlineResults*/);
} else {
verify(mockOfflineCb, times(1)).onReady(offlineSession);
verify(mockOfflineCb, times(0)).onSwitchFailed(offlineSession);
- if (closeDevice) {
- // According to specification, closing the initial camera device after
- // successful offline session switch must not have any noticeable impact
- // on the offline client.
- closeDevice();
+ switch (testSequence) {
+ case CloseDeviceAndOpenRemote:
+ // According to the documentation, closing the initial camera device and
+ // re-opening the same device from a different client after successful
+ // offline session switch must not have any noticeable impact on the
+ // offline processing.
+ closeDevice();
+ remoteOfflinePID = startRemoteOfflineTestProcess(cameraId);
+
+ break;
+ case CloseOfflineSession:
+ offlineSession.close();
+
+ break;
+ case InitializeRegularSession:
+ // According to the documentation, initializing a regular capture session
+ // along with the offline session should not have any side effects.
+ // We also don't want to re-use the same offline output surface as part
+ // of the new regular capture session.
+ outputSurfaces.remove(mReaderSurface);
+
+ // According to the specification, an active offline session must allow
+ // camera devices to support at least one preview stream, one yuv stream
+ // of size up-to 1080p, one jpeg stream with any supported size and
+ // an extra input/output private pair in case reprocessing is also available.
+ yuvCallbackReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
+ 1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+ outputSurfaces.add(yuvCallbackReader.getSurface());
+
+ jpegReader = makeImageReader(offlineSize, ImageFormat.JPEG,
+ 1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+ outputSurfaces.add(jpegReader.getSurface());
+
+ if (mAllStaticInfo.get(cameraId).isPrivateReprocessingSupported()) {
+ privateReader = makeImageReader(privateSize, ImageFormat.PRIVATE,
+ 1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+ outputSurfaces.add(privateReader.getSurface());
+
+ InputConfiguration inputConfig = new InputConfiguration(
+ privateSize.getWidth(), privateSize.getHeight(),
+ ImageFormat.PRIVATE);
+ mSession = CameraTestUtils.configureReprocessableCameraSession(mCamera,
+ inputConfig, outputSurfaces, mSessionListener, mHandler);
+
+ } else {
+ mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener,
+ mHandler);
+ }
+
+ mSession.setRepeatingRequest(previewRequest.build(), regularResultListener,
+ mHandler);
+
+ break;
+ case NoExtraSteps:
+ default:
}
- if (closeSession) {
- offlineSession.close();
- }
+ // The repeating non-offline request should be done after the switch returns.
+ verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
+ false /*offlineResults*/);
- // The repeating non-offline request should be completed after the switch returns.
- verifyCaptureResults(resultListener, repeatingSeqId, false /*offlineResults*/);
-
- if (!closeSession) {
+ if (testSequence != OfflineTestSequence.CloseOfflineSession) {
offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_IDLE,
WAIT_FOR_STATE_TIMEOUT_MS);
verify(mockOfflineCb, times(1)).onIdle(offlineSession);
verify(mockOfflineCb, times(0)).onError(offlineSession,
CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
- // The offline requests should be completed after we reach idle state.
- verifyCaptureResults(offlineResultListener, offlineSeqId, true /*offlineResults*/);
+ // The offline requests should be done after we reach idle state.
+ verifyCaptureResults(offlineResultListener, imageListener, offlineSeqId,
+ true /*offlineResults*/);
offlineSession.close();
- offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_CLOSED,
- WAIT_FOR_STATE_TIMEOUT_MS);
}
+ if (testSequence == OfflineTestSequence.InitializeRegularSession) {
+ checkInitialResults(regularResultListener);
+ stopPreview();
+
+ if (privateReader != null) {
+ privateReader.close();
+ }
+
+ if (yuvCallbackReader != null) {
+ yuvCallbackReader.close();
+ }
+
+ if (jpegReader != null) {
+ jpegReader.close();
+ }
+ }
+
+ offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_CLOSED,
+ WAIT_FOR_STATE_TIMEOUT_MS);
verify(mockOfflineCb, times(1)).onClosed(offlineSession);
}
closeImageReader();
+
+ stopRemoteOfflineTestProcess(remoteOfflinePID);
+ }
+
+ private void checkInitialResults(SimpleCaptureCallback resultListener) {
+ CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
+
+ Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+ assertNotNull("Can't read a capture result timestamp", timestamp);
+
+ CaptureResult result2 = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
+
+ Long timestamp2 = result2.get(CaptureResult.SENSOR_TIMESTAMP);
+ assertNotNull("Can't read a capture result 2 timestamp", timestamp2);
+
+ assertTrue("Bad timestamps", timestamp2 > timestamp);
}
private void camera2UnsupportedOfflineOutputTest(boolean useSurfaceGroup) throws Exception {
@@ -574,4 +749,56 @@
session.close();
}
+
+ private int startRemoteOfflineTestProcess(String cameraId) throws InterruptedException {
+ // Ensure no running activity process with same name
+ String cameraActivityName = mContext.getPackageName() + ":" + REMOTE_PROCESS_NAME;
+ ActivityManager activityManager = (ActivityManager) mContext.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningAppProcessInfo> list = activityManager.getRunningAppProcesses();
+ for (ActivityManager.RunningAppProcessInfo rai : list) {
+ if (cameraActivityName.equals(rai.processName)) {
+ fail("Remote offline session test activity already running");
+ return -1;
+ }
+ }
+
+ Activity activity = mActivityRule.getActivity();
+ Intent activityIntent = new Intent(activity, REMOTE_PROCESS_CLASS);
+ Bundle b = new Bundle();
+ b.putString(CameraTestUtils.OFFLINE_CAMERA_ID, cameraId);
+ activityIntent.putExtras(b);
+ activity.startActivity(activityIntent);
+ Thread.sleep(WAIT_FOR_REMOTE_ACTIVITY_LAUNCH_MS);
+
+ // Fail if activity isn't running
+ list = activityManager.getRunningAppProcesses();
+ for (ActivityManager.RunningAppProcessInfo rai : list) {
+ if (cameraActivityName.equals(rai.processName))
+ return rai.pid;
+ }
+
+ fail("Remote offline session test activity failed to start");
+
+ return -1;
+ }
+
+ private void stopRemoteOfflineTestProcess(int remotePID) throws InterruptedException {
+ if (remotePID < 0) {
+ return;
+ }
+
+ android.os.Process.killProcess(remotePID);
+ Thread.sleep(WAIT_FOR_REMOTE_ACTIVITY_DESTROY_MS);
+
+ ActivityManager activityManager = (ActivityManager) mContext.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ String cameraActivityName = mContext.getPackageName() + ":" + REMOTE_PROCESS_NAME;
+ List<ActivityManager.RunningAppProcessInfo> list = activityManager.getRunningAppProcesses();
+ for (ActivityManager.RunningAppProcessInfo rai : list) {
+ if (cameraActivityName.equals(rai.processName))
+ fail("Remote offline session test activity is still running");
+ }
+
+ }
}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index f64ff08..da6593f 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -120,6 +120,8 @@
public static final int MAX_READER_IMAGES = 5;
+ public static final String OFFLINE_CAMERA_ID = "offline_camera_id";
+
private static final int EXIF_DATETIME_LENGTH = 19;
private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f;
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index bcad582..a5bedea 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -2082,6 +2082,45 @@
}
/**
+ * Determine whether the current device supports a private reprocessing capability or not.
+ *
+ * @return {@code true} if the capability is supported, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if {@code capability} was negative
+ */
+ public boolean isPrivateReprocessingSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+ }
+
+ /**
+ * Get sorted (descending order) size list for given input format. Remove the sizes larger than
+ * the bound. If the bound is null, don't do the size bound filtering.
+ *
+ * @param format input format
+ * @param bound maximum allowed size bound
+ *
+ * @return Sorted input size list (descending order)
+ */
+ public List<Size> getSortedSizesForInputFormat(int format, Size bound) {
+ Size[] availableSizes = getAvailableSizesForFormatChecked(format, StreamDirection.Input);
+ if (bound == null) {
+ return CameraTestUtils.getAscendingOrderSizes(Arrays.asList(availableSizes),
+ /*ascending*/false);
+ }
+
+ List<Size> sizes = new ArrayList<Size>();
+ for (Size sz: availableSizes) {
+ if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+ sizes.add(sz);
+ }
+ }
+
+ return CameraTestUtils.getAscendingOrderSizes(sizes, /*ascending*/false);
+ }
+
+
+ /**
* Determine whether or not all the {@code keys} are available characteristics keys
* (as in {@link CameraCharacteristics#getKeys}.
*
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 2ff0d87..957062b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -73,7 +73,6 @@
* atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationTests
*/
@Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
public class WindowInsetsAnimationTests extends WindowManagerTestBase {
TestActivity mActivity;
@@ -117,6 +116,7 @@
}
@Test
+ @FlakyTest(detail = "Promote once confirmed non-flaky")
public void testAnimationCallbacks_overlapping() {
WindowInsets before = mActivity.mLastWindowInsets;
diff --git a/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index 5eb3e36..29f091b 100644
--- a/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -64,6 +64,9 @@
// wait for Wi-Fi Aware state changes & network requests callbacks
static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10; // 10 seconds
+ private static final int MIN_DISTANCE_MM = 1 * 1000;
+ private static final int MAX_DISTANCE_MM = 3 * 1000;
+ private static final byte[] PMK_VALID = "01234567890123456789012345678901".getBytes();
private final Object mLock = new Object();
private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
@@ -615,7 +618,8 @@
// 2. update-subscribe
subscribeConfig = new SubscribeConfig.Builder().setServiceName(
- serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+ serviceName).setServiceSpecificInfo("extras".getBytes())
+ .setMinDistanceMm(MIN_DISTANCE_MM).build();
discoverySession.updateSubscribe(subscribeConfig);
assertTrue("Subscribe update", discoveryCb.waitForCallback(
DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
@@ -741,7 +745,8 @@
}
/**
- * Request an Aware data-path (encrypted) as a Responder with an arbitrary peer MAC address.
+ * Request an Aware data-path (encrypted with Passphrase) as a Responder with an arbitrary peer
+ * MAC address.
* Validate that receive an onUnavailable() callback.
*/
public void testDataPathPassphraseOutOfBandFail() {
@@ -773,6 +778,40 @@
session.close();
}
+ /**
+ * Request an Aware data-path (encrypted with PMK) as a Responder with an arbitrary peer MAC
+ * address.
+ * Validate that receive an onUnavailable() callback.
+ */
+ public void testDataPathPmkOutOfBandFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+ MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
+
+ // 1. initialize Aware: only purpose is to make sure it is available for OOB data-path
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+
+ // 2. request an AWARE network
+ NetworkCallbackTest networkCb = new NetworkCallbackTest();
+ NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+ session.createNetworkSpecifierPmk(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, mac.toByteArray(),
+ PMK_VALID)).build();
+ mConnectivityManager.requestNetwork(nr, networkCb);
+ assertTrue("OnUnavailable not received", networkCb.waitForOnUnavailable());
+
+ session.close();
+ }
+
// local utilities
private WifiAwareSession attachAndGetSession() {
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 7dcf97b..da3cc11 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -727,7 +727,7 @@
<!-- ====================================================================== -->
<eat-comment />
- <!-- @SystemApi Allows accessing the messages on ICC
+ <!-- Allows accessing the messages on ICC
@hide Used internally. -->
<permission android:name="android.permission.ACCESS_MESSAGES_ON_ICC"
android:protectionLevel="signature" />
diff --git a/tests/tests/view/surfacevalidator/Android.mk b/tests/tests/view/surfacevalidator/Android.mk
index fbb6894..973d374 100644
--- a/tests/tests/view/surfacevalidator/Android.mk
+++ b/tests/tests/view/surfacevalidator/Android.mk
@@ -25,6 +25,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.test.rules \
+ cts-wm-util \
ub-uiautomator
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 4d6bab5..51ef4a0 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -36,6 +36,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.provider.Settings;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
@@ -49,6 +50,8 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.server.wm.settings.SettingsSession;
+
import androidx.test.InstrumentationRegistry;
import org.junit.rules.TestName;
@@ -66,6 +69,15 @@
public final SparseArray<Bitmap> failures = new SparseArray<>();
}
+ 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;
+
private static final String TAG = "CapturedActivity";
private static final int PERMISSION_CODE = 1;
private MediaProjectionManager mProjectionManager;
@@ -100,6 +112,9 @@
// longer duration to capture the expected number of frames
mOnEmbedded = packageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
+ mSettingsSession = new ImmersiveConfirmationSetting();
+ mSettingsSession.set("confirmed");
+
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
// Set the NULL pointer icon so that it won't obstruct the captured image.
@@ -115,6 +130,11 @@
bindMediaProjectionService();
}
+ @Override
+ public void onStop() {
+ mSettingsSession.close();
+ }
+
public void dismissPermissionDialog() {
// The permission dialog will be auto-opened by the activity - find it and accept
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 47b120b..9bdb58f 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -22,6 +22,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWidgetTestCases.apk" />
+ <option name="test-file-name" value="CtsWidgetApp.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.widget.cts" />
diff --git a/tests/tests/widget/app/Android.bp b/tests/tests/widget/app/Android.bp
new file mode 100644
index 0000000..819e4a0
--- /dev/null
+++ b/tests/tests/widget/app/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: "CtsWidgetApp",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "test_current",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+}
diff --git a/tests/tests/widget/app/AndroidManifest.xml b/tests/tests/widget/app/AndroidManifest.xml
new file mode 100755
index 0000000..9c19ce1
--- /dev/null
+++ b/tests/tests/widget/app/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.widget.cts.app">
+
+ <application>
+ <activity
+ android:name=".TranslucentActivity"
+ android:exported="true"
+ android:theme="@style/TranslucentTheme" />
+ </application>
+</manifest>
diff --git a/tests/tests/widget/app/res/values/styles.xml b/tests/tests/widget/app/res/values/styles.xml
new file mode 100644
index 0000000..a38f6b2
--- /dev/null
+++ b/tests/tests/widget/app/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<resources>
+ <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
+</resources>
diff --git a/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java
new file mode 100644
index 0000000..c9b79b3
--- /dev/null
+++ b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java
@@ -0,0 +1,87 @@
+/*
+ * 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.widget.cts.app;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+public class TranslucentActivity extends AppCompatActivity {
+ private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED =
+ "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED";
+ private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH =
+ "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager.findFragmentByTag("dialog") == null) {
+ DialogFragment fragment = new SampleFragment();
+ fragment.show(fragmentManager, "dialog");
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_TRANSLUCENT_ACTIVITY_FINISH);
+ registerReceiver(mFinishActivityReceiver, filter);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_RESUMED));
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ unregisterReceiver(mFinishActivityReceiver);
+ }
+
+ private final BroadcastReceiver mFinishActivityReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ finish();
+ }
+ };
+
+ public static class SampleFragment extends DialogFragment {
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setTitle("Title")
+ .setMessage("Message")
+ .setOnDismissListener(dialog -> getActivity().finish())
+ .create();
+ }
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index dc9f732..896eade 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -24,7 +26,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.ConditionVariable;
@@ -71,6 +77,13 @@
private static final int ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS = 3000;
private static final long TIME_FOR_UI_OPERATION = 1000L;
private static final long TIME_OUT = 5000L;
+ private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED =
+ "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED";
+ private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH =
+ "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH";
+ private static final ComponentName COMPONENT_TRANSLUCENT_ACTIVITY =
+ ComponentName.unflattenFromString("android.widget.cts.app/.TranslucentActivity");
+
private Toast mToast;
private Context mContext;
private boolean mLayoutDone;
@@ -650,6 +663,24 @@
assertNotShowCustomToast(view);
}
+ @Test
+ public void testCustomToastBlocked_whenBehindTranslucentActivity() throws Throwable {
+ ConditionVariable activityStarted = registerBlockingReceiver(
+ ACTION_TRANSLUCENT_ACTIVITY_RESUMED);
+ Intent intent = new Intent();
+ intent.setComponent(COMPONENT_TRANSLUCENT_ACTIVITY);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ activityStarted.block();
+ makeCustomToast();
+ View view = mToast.getView();
+
+ mActivityRule.runOnUiThread(mToast::show);
+
+ assertNotShowCustomToast(view);
+ mContext.sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_FINISH));
+ }
+
@UiThreadTest
@Test
public void testGetWindowParams_whenTextToast_returnsNull() {
@@ -665,6 +696,18 @@
assertNotNull(toast.getWindowParams());
}
+ private ConditionVariable registerBlockingReceiver(String action) {
+ ConditionVariable broadcastReceived = new ConditionVariable(false);
+ IntentFilter filter = new IntentFilter(action);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ broadcastReceived.open();
+ }
+ }, filter);
+ return broadcastReceived;
+ }
+
private void runOnMainAndDrawSync(@NonNull final View toastView,
@Nullable final Runnable runner) {
final CountDownLatch latch = new CountDownLatch(1);