| /* |
| * 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.suspendapps.cts; |
| |
| import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND; |
| import static android.suspendapps.cts.Constants.TEST_APP_PACKAGE_NAME; |
| import static android.suspendapps.cts.SuspendTestUtils.assertSameExtras; |
| import static android.suspendapps.cts.SuspendTestUtils.createExtras; |
| import static android.suspendapps.cts.SuspendTestUtils.startTestAppActivity; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.SuspendDialogInfo; |
| import android.os.Bundle; |
| import android.platform.test.annotations.SystemUserOnly; |
| import android.support.test.uiautomator.By; |
| import android.support.test.uiautomator.UiDevice; |
| import android.support.test.uiautomator.UiObject2; |
| import android.support.test.uiautomator.Until; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.SystemUtil; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.concurrent.SynchronousQueue; |
| import java.util.concurrent.TimeUnit; |
| import java.util.regex.Pattern; |
| |
| @RunWith(AndroidJUnit4.class) |
| public class DialogTests { |
| private static final String TEST_APP_LABEL = "Suspend Test App"; |
| private static final long UI_TIMEOUT_MS = 60_000; |
| |
| /** Used to poll for the intents sent by the system to this package */ |
| static final SynchronousQueue<Intent> sIncomingIntent = new SynchronousQueue<>(); |
| |
| private Context mContext; |
| private TestAppInterface mTestAppInterface; |
| private UiDevice mUiDevice; |
| |
| @Before |
| public void setUp() throws Exception { |
| mContext = InstrumentationRegistry.getTargetContext(); |
| mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); |
| mTestAppInterface = new TestAppInterface(mContext); |
| turnScreenOn(); |
| } |
| |
| private void turnScreenOn() throws Exception { |
| if (!mUiDevice.isScreenOn()) { |
| mUiDevice.wakeUp(); |
| } |
| SystemUtil.runShellCommandForNoOutput("wm dismiss-keyguard"); |
| } |
| |
| @Test |
| public void testInterceptorActivity_unsuspend() throws Exception { |
| final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder() |
| .setIcon(R.drawable.ic_settings) |
| .setTitle(R.string.dialog_title) |
| .setMessage(R.string.dialog_message) |
| .setNeutralButtonText(R.string.unsuspend_button_text) |
| .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND) |
| .build(); |
| SuspendTestUtils.suspend(null, null, dialogInfo); |
| // Ensure test app's activity is stopped before proceeding. |
| assertTrue(mTestAppInterface.awaitTestActivityStop()); |
| |
| final Bundle extrasForStart = new Bundle(createExtras("unsuspend", 90, "sval", 0.9)); |
| startTestAppActivity(extrasForStart); |
| // Test activity should not start. |
| assertNull("Test activity started while suspended", |
| mTestAppInterface.awaitTestActivityStart(5_000)); |
| |
| final String expectedTitle = mContext.getResources().getString(R.string.dialog_title); |
| final String expectedButtonText = mContext.getResources().getString( |
| R.string.unsuspend_button_text); |
| |
| assertNotNull("Given dialog title \"" + expectedTitle + "\" not shown", |
| mUiDevice.wait(Until.findObject(By.text(expectedTitle)), UI_TIMEOUT_MS)); |
| |
| // Sometimes, button texts can have styles that override case (e.g. b/134033532) |
| final Pattern buttonTextIgnoreCase = Pattern.compile(Pattern.quote(expectedButtonText), |
| Pattern.CASE_INSENSITIVE); |
| final UiObject2 unsuspendButton = mUiDevice.findObject( |
| By.clickable(true).text(buttonTextIgnoreCase)); |
| assertNotNull("\"" + expectedButtonText + "\" button not shown", unsuspendButton); |
| |
| // Tapping on the neutral button should: |
| // 1. Tell the suspending package that the test app was unsuspended |
| // 2. Launch the previously intercepted intent |
| // 3. Unsuspend the test app |
| unsuspendButton.click(); |
| |
| final Intent incomingIntent = sIncomingIntent.poll(30, TimeUnit.SECONDS); |
| assertNotNull(incomingIntent); |
| assertEquals(Intent.ACTION_PACKAGE_UNSUSPENDED_MANUALLY, incomingIntent.getAction()); |
| assertEquals("Did not receive correct unsuspended package name", TEST_APP_PACKAGE_NAME, |
| incomingIntent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)); |
| |
| final Intent activityIntent = mTestAppInterface.awaitTestActivityStart(); |
| assertNotNull("Test activity did not start on neutral button tap", activityIntent); |
| assertSameExtras("Different extras passed to startActivity on unsuspend", |
| extrasForStart, activityIntent.getExtras()); |
| |
| assertFalse("Test package still suspended", mTestAppInterface.isTestAppSuspended()); |
| } |
| |
| @Test |
| public void testInterceptorActivity_moreDetails() throws Exception { |
| final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder() |
| .setIcon(R.drawable.ic_settings) |
| .setTitle(R.string.dialog_title) |
| .setMessage(R.string.dialog_message) |
| .setNeutralButtonText(R.string.more_details_button_text) |
| .build(); |
| SuspendTestUtils.suspend(null, null, dialogInfo); |
| // Ensure test app's activity is stopped before proceeding. |
| assertTrue(mTestAppInterface.awaitTestActivityStop()); |
| |
| startTestAppActivity(null); |
| // Test activity should not start. |
| assertNull("Test activity started while suspended", |
| mTestAppInterface.awaitTestActivityStart(5_000)); |
| |
| // The dialog should have correct specifications |
| final String expectedTitle = mContext.getResources().getString(R.string.dialog_title); |
| final String expectedMessage = mContext.getResources().getString(R.string.dialog_message, |
| TEST_APP_LABEL); |
| final String expectedButtonText = mContext.getResources().getString( |
| R.string.more_details_button_text); |
| |
| assertNotNull("Given dialog title: " + expectedTitle + " not shown", |
| mUiDevice.wait(Until.findObject(By.text(expectedTitle)), UI_TIMEOUT_MS)); |
| assertNotNull("Given dialog message: " + expectedMessage + " not shown", |
| mUiDevice.findObject(By.text(expectedMessage))); |
| // Sometimes, button texts can have styles that override case (e.g. b/134033532) |
| final Pattern buttonTextIgnoreCase = Pattern.compile(Pattern.quote(expectedButtonText), |
| Pattern.CASE_INSENSITIVE); |
| final UiObject2 moreDetailsButton = mUiDevice.findObject( |
| By.clickable(true).text(buttonTextIgnoreCase)); |
| assertNotNull(expectedButtonText + " button not shown", moreDetailsButton); |
| |
| // Tapping on the neutral button should start the correct intent. |
| moreDetailsButton.click(); |
| final Intent incomingIntent = sIncomingIntent.poll(30, TimeUnit.SECONDS); |
| assertNotNull(incomingIntent); |
| assertEquals(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS, incomingIntent.getAction()); |
| assertEquals("Wrong package name sent with " + Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS, |
| TEST_APP_PACKAGE_NAME, incomingIntent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)); |
| } |
| |
| @Test |
| public void testInterceptorActivity_strings() throws Exception { |
| // The dialog should have correct specifications |
| final String expectedTitle = "Test Dialog Title"; |
| final String expectedMessage = "This is a test message"; |
| final String expectedButtonText = "Test button"; |
| |
| final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder() |
| .setIcon(R.drawable.ic_settings) |
| .setTitle(expectedTitle) |
| .setMessage(expectedMessage) |
| .setNeutralButtonText(expectedButtonText) |
| .build(); |
| SuspendTestUtils.suspend(null, null, dialogInfo); |
| // Ensure test app's activity is stopped before proceeding. |
| assertTrue(mTestAppInterface.awaitTestActivityStop()); |
| |
| startTestAppActivity(null); |
| // Test activity should not start. |
| assertNull("Test activity started while suspended", |
| mTestAppInterface.awaitTestActivityStart(5_000)); |
| |
| |
| assertNotNull("Given dialog title: " + expectedTitle + " not shown", |
| mUiDevice.wait(Until.findObject(By.text(expectedTitle)), UI_TIMEOUT_MS)); |
| assertNotNull("Given dialog message: " + expectedMessage + " not shown", |
| mUiDevice.findObject(By.text(expectedMessage))); |
| // Sometimes, button texts can have styles that override case (e.g. b/134033532) |
| final Pattern buttonTextIgnoreCase = Pattern.compile(Pattern.quote(expectedButtonText), |
| Pattern.CASE_INSENSITIVE); |
| final UiObject2 moreDetailsButton = mUiDevice.findObject( |
| By.clickable(true).text(buttonTextIgnoreCase)); |
| assertNotNull(expectedButtonText + " button not shown", moreDetailsButton); |
| } |
| |
| @After |
| public void tearDown() { |
| if (mTestAppInterface != null) { |
| mTestAppInterface.disconnect(); |
| } |
| mUiDevice.pressBack(); |
| mUiDevice.pressHome(); |
| } |
| } |